diff --git a/.travis.yml b/.travis.yml index f4b52bae9457..130e907c5cc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,24 +17,28 @@ sudo: required dist: trusty group: edge + language: java jdk: -- oraclejdk8 + - oraclejdk8 python: - "2.7" + cache: directories: - - $HOME/.m2 + - $HOME/.m2 timeout: 500 + notifications: email: false + env: global: - PATH=$HOME/.local/bin:$PATH matrix: # Keep the TESTS sorted by name and grouped by type - TESTS="smoke/test_certauthority_root" - + - TESTS="smoke/test_accounts smoke/test_affinity_groups smoke/test_affinity_groups_projects @@ -43,6 +47,7 @@ env: smoke/test_deploy_vm_root_resize smoke/test_deploy_vm_with_userdata smoke/test_deploy_vms_with_varied_deploymentplanners + smoke/test_diagnostics smoke/test_disk_offerings smoke/test_dynamicroles smoke/test_global_settings @@ -171,3 +176,46 @@ script: after_success: ./tools/travis/after_success.sh after_failure: ./tools/travis/after_failure.sh after_script: ./tools/travis/after_script.sh + +# Packaging job definition, will be reused +.package_job: &package_job + before_install: docker pull ${IMAGE} + install: true + before_script: true + script: | + docker run \ + --volume ${TRAVIS_BUILD_DIR}:/mnt/build/cloudstack \ + --volume $HOME/.m2:/root/.m2 \ + --rm \ + ${IMAGE} ${PARAMS} + after_script: true + after_success: true + after_failure: true + +jobs: + include: + - stage: package + services: docker + env: IMAGE=khos2ow/cloudstack-rpm-builder:centos7 PARAMS="--distribution centos7 --pack oss" + <<: *package_job + + - stage: package + services: docker + env: IMAGE=khos2ow/cloudstack-rpm-builder:centos6 PARAMS="--distribution centos63 --pack oss" + <<: *package_job + + - stage: package + services: docker + env: IMAGE=khos2ow/cloudstack-deb-builder:ubuntu1804 PARAMS="" + <<: *package_job + + - stage: package + services: docker + env: IMAGE=khos2ow/cloudstack-deb-builder:ubuntu1604 PARAMS="" + <<: *package_job + + - stage: package + services: docker + env: IMAGE=khos2ow/cloudstack-deb-builder:ubuntu1404 PARAMS="" + <<: *package_job + diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java index 84a6bf5fc16e..e7c6c29c1ce2 100644 --- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java @@ -72,6 +72,18 @@ public class VirtualMachineTO { Double cpuQuotaPercentage = null; + boolean enableExtraConfig = false; + + public boolean isExtraConfigEnabled() { + return enableExtraConfig; + } + + public String getExtraConfig() { + return extraConfig; + } + + String extraConfig = null; + Map guestOsDetails = new HashMap(); public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader, @@ -350,4 +362,12 @@ public Double getCpuQuotaPercentage() { public void setCpuQuotaPercentage(Double cpuQuotaPercentage) { this.cpuQuotaPercentage = cpuQuotaPercentage; } + + public void setEnableExtraConfig(boolean enableExtraConfig) { + this.enableExtraConfig = enableExtraConfig; + } + + public void setExtraConfig(String extraConfig) { + this.extraConfig = extraConfig; + } } diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index cf13cd671a91..ec20c33e17eb 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -102,4 +102,37 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc void updateDisplay(Volume volume, Boolean displayVolume); Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName) throws ResourceAllocationException; -} + + /** + * Checks if the target storage supports the disk offering. + * This validation is consistent with the mechanism used to select a storage pool to deploy a volume when a virtual machine is deployed or when a data disk is allocated. + * + * The scenarios when this method returns true or false is presented in the following table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
#Disk offering tagsStorage tagsDoes the storage support the disk offering?
1A,BANO
2A,B,CA,B,C,D,XYES
3A,B,CX,Y,ZNO
4nullA,S,DYES
5AnullNO
6nullnullYES
+ */ + boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags); +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 504b2149837e..58969c542cf5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -155,6 +155,7 @@ public class ApiConstants { public static final String IDS = "ids"; public static final String PREVIOUS_ACL_RULE_ID = "previousaclruleid"; public static final String NEXT_ACL_RULE_ID = "nextaclruleid"; + public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash"; public static final String INTERNAL_DNS1 = "internaldns1"; public static final String INTERNAL_DNS2 = "internaldns2"; public static final String INTERVAL_TYPE = "intervaltype"; @@ -719,6 +720,13 @@ public class ApiConstants { public static final String LAST_ANNOTATED = "lastannotated"; public static final String LDAP_DOMAIN = "ldapdomain"; + public static final String STDOUT = "stdout"; + public static final String STDERR = "stderr"; + public static final String EXITCODE = "exitcode"; + public static final String TARGET_ID = "targetid"; + + public static final String EXTRA_CONFIG = "extraconfig"; + public enum HostDetails { all, capacity, events, stats, min; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RunDiagnosticsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RunDiagnosticsCmd.java new file mode 100644 index 000000000000..bb1ddf57cb7d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RunDiagnosticsCmd.java @@ -0,0 +1,136 @@ +// 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 org.apache.cloudstack.api.command.admin.diagnostics; + +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.RunDiagnosticsResponse; +import org.apache.cloudstack.api.response.SystemVmResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.diagnostics.DiagnosticsService; +import org.apache.cloudstack.diagnostics.DiagnosticsType; +import org.apache.commons.collections.CollectionUtils; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.Collections; +import java.util.Map; + +@APICommand(name = RunDiagnosticsCmd.APINAME, responseObject = RunDiagnosticsResponse.class, entityType = {VirtualMachine.class}, + responseHasSensitiveInfo = false, + requestHasSensitiveInfo = false, + description = "Execute network-utility command (ping/arping/tracert) on system VMs remotely", + authorized = {RoleType.Admin}, + since = "4.12.0.0") +public class RunDiagnosticsCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(RunDiagnosticsCmd.class); + public static final String APINAME = "runDiagnostics"; + + @Inject + private DiagnosticsService diagnosticsService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.TARGET_ID, type = CommandType.UUID, required = true, entityType = SystemVmResponse.class, + validations = {ApiArgValidator.PositiveNumber}, + description = "The ID of the system VM instance to diagnose") + private Long id; + + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = true, + validations = {ApiArgValidator.NotNullOrEmpty}, + description = "The IP/Domain address to test connection to") + private String address; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, + validations = {ApiArgValidator.NotNullOrEmpty}, + description = "The system VM diagnostics type valid options are: ping, traceroute, arping") + private String type; + + @Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, + authorized = {RoleType.Admin}, + description = "Additional command line options that apply for each command") + private String optionalArguments; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getId() { + return id; + } + + public String getAddress() { + return address; + } + + public DiagnosticsType getType() { + DiagnosticsType diagnosticsType = DiagnosticsType.getCommand(type); + if (diagnosticsType == null) { + throw new IllegalArgumentException(type + " Is not a valid diagnostics command type. "); + } + return diagnosticsType; + } + + public String getOptionalArguments() { + return optionalArguments; + } + + ///////////////////////////////////////////////////// + /////////////////// Implementation ////////////////// + ///////////////////////////////////////////////////// + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + if (account != null) { + return account.getId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException { + RunDiagnosticsResponse response = new RunDiagnosticsResponse(); + try { + final Map answerMap = diagnosticsService.runDiagnosticsCommand(this); + if (CollectionUtils.isNotEmpty(Collections.singleton(answerMap))) { + response.setStdout(answerMap.get(ApiConstants.STDOUT)); + response.setStderr(answerMap.get(ApiConstants.STDERR)); + response.setExitCode(answerMap.get(ApiConstants.EXITCODE)); + response.setObjectName("diagnostics"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + } catch (final ServerApiException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java index aaa9c185526c..0343e5012e4a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/MoveNetworkAclItemCmd.java @@ -43,6 +43,9 @@ public class MoveNetworkAclItemCmd extends BaseAsyncCustomIdCmd { @Parameter(name = ApiConstants.NEXT_ACL_RULE_ID, type = CommandType.STRING, description = "The ID of the rule that is right after the new position where the rule being moved is going to be placed. This value can be 'NULL' if the rule is being moved to the last position of the network ACL list.") private String nextAclRuleUuid; + @Parameter(name = ApiConstants.MOVE_ACL_CONSISTENCY_HASH, type = CommandType.STRING, description = "Md5 hash used to check the consistency of the ACL rule list before applying the ACL rule move. This check is useful to manage concurrency problems that may happen when multiple users are editing the same ACL rule listing. The parameter is not required. Therefore, if the user does not send it, he/she is assuming the risk of moving ACL rules without checking the consistency of the access control list before executing the move. We use MD5 hash function on a String that is composed of all UUIDs of the ACL rules in concatenated in their respective order (order defined via 'number' field).") + private String aclConsistencyHash; + @Override public void execute() { CallContext.current().setEventDetails(getEventDescription()); @@ -93,4 +96,8 @@ public long getEntityOwnerId() { Account caller = CallContext.current().getCallingAccount(); return caller.getAccountId(); } -} + + public String getAclConsistencyHash() { + return aclConsistencyHash; + } +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 29d4c97ce0d2..b2f9dcf2c69a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -200,6 +200,9 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter") private Map dataDiskTemplateToDiskOfferingList; + @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", authorized = { RoleType.Admin }, length = 5120) + private String extraConfig; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -482,6 +485,10 @@ public Map getDataDiskTemplateToDiskOfferingMap() { return dataDiskTemplateToDiskOfferingMap; } + public String getExtraConfig() { + return extraConfig; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java index fa7488f15bff..3d22dfe092ce 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtractResponse.java @@ -66,9 +66,6 @@ public class ExtractResponse extends BaseResponse { @Param(description = "type of the storage") private String storageType; - @SerializedName("storage") - private String storage; - @SerializedName(ApiConstants.ZONE_ID) @Param(description = "zone ID the object was extracted from") private String zoneId; @@ -176,14 +173,6 @@ public void setStorageType(String storageType) { this.storageType = storageType; } - public String getStorage() { - return storage; - } - - public void setStorage(String storage) { - this.storage = storage; - } - public String getZoneId() { return zoneId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RunDiagnosticsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RunDiagnosticsResponse.java new file mode 100644 index 000000000000..4c8a923613ab --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/RunDiagnosticsResponse.java @@ -0,0 +1,67 @@ +// +// 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 org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +@EntityReference(value = VirtualMachine.class) +public class RunDiagnosticsResponse extends BaseResponse { + @SerializedName(ApiConstants.STDOUT) + @Param(description = "the standard output from the command execution") + private String stdout; + + @SerializedName(ApiConstants.STDERR) + @Param(description = "the standard error output from the command execution") + private String stderr; + + @SerializedName(ApiConstants.EXITCODE) + @Param(description = "the command execution return code") + private String exitCode; + + public String getStdout() { + return stdout; + } + + public void setStdout(String stdout) { + this.stdout = stdout; + } + + public String getStderr() { + return stderr; + } + + public void setStderr(String stderr) { + this.stderr = stderr; + } + + public String getExitCode() { + return exitCode; + } + + public void setExitCode(String exitCode) { + this.exitCode = exitCode; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java index 61bab02d321e..efd3b79ed0a2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java @@ -73,10 +73,6 @@ public class ZoneResponse extends BaseResponse { @Param(description = "the guest CIDR address for the Zone") private String guestCidrAddress; - //TODO - generate description - @SerializedName("status") - private String status; - @SerializedName(ApiConstants.DISPLAY_TEXT) @Param(description = "the display text of the zone") private String displayText; @@ -165,10 +161,6 @@ public void setGuestCidrAddress(String guestCidrAddress) { this.guestCidrAddress = guestCidrAddress; } - public void setStatus(String status) { - this.status = status; - } - public void setDisplayText(String displayText) { this.displayText = displayText; } @@ -272,10 +264,6 @@ public String getGuestCidrAddress() { return guestCidrAddress; } - public String getStatus() { - return status; - } - public String getDisplayText() { return displayText; } diff --git a/api/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsService.java b/api/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsService.java new file mode 100644 index 000000000000..a9177af7e0c9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsService.java @@ -0,0 +1,29 @@ +// +// 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 org.apache.cloudstack.diagnostics; + +import org.apache.cloudstack.api.command.admin.diagnostics.RunDiagnosticsCmd; + +import java.util.Map; + +public interface DiagnosticsService { + + Map runDiagnosticsCommand(RunDiagnosticsCmd cmd); + +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsType.java b/api/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsType.java new file mode 100644 index 000000000000..0e3a1dad2c62 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsType.java @@ -0,0 +1,42 @@ +// +// 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 org.apache.cloudstack.diagnostics; + +public enum DiagnosticsType { + PING("ping"), TRACEROUTE("traceroute"), ARPING("arping"); + + private String value; + + DiagnosticsType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static DiagnosticsType getCommand(String cmd) { + for (DiagnosticsType type : DiagnosticsType.values()) { + if (type.value.equalsIgnoreCase(cmd)) { + return type; + } + } + return null; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java index 838f0877918f..2c75a78b1a38 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java @@ -69,4 +69,5 @@ public class VRScripts { public static final String VR_CFG = "vr_cfg.sh"; + public static final String DIAGNOSTICS = "diagnostics.py"; } \ No newline at end of file diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 0ffe8cc0ea2f..112d9209349e 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -22,6 +22,9 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel; + +import org.apache.cloudstack.diagnostics.DiagnosticsAnswer; +import org.apache.cloudstack.diagnostics.DiagnosticsCommand; import org.joda.time.Duration; import java.util.ArrayList; import java.util.HashMap; @@ -189,9 +192,11 @@ private Answer executeQueryCommand(NetworkElementCommand cmd) { } else if (cmd instanceof GetDomRVersionCmd) { return execute((GetDomRVersionCmd)cmd); } else if (cmd instanceof CheckS2SVpnConnectionsCommand) { - return execute((CheckS2SVpnConnectionsCommand) cmd); + return execute((CheckS2SVpnConnectionsCommand)cmd); } else if (cmd instanceof GetRouterAlertsCommand) { return execute((GetRouterAlertsCommand)cmd); + } else if (cmd instanceof DiagnosticsCommand) { + return execute((DiagnosticsCommand)cmd); } else { s_logger.error("Unknown query command in VirtualRoutingResource!"); return Answer.createUnsupportedCommandAnswer(cmd); @@ -292,6 +297,15 @@ private Answer execute(CheckRouterCommand cmd) { return new CheckRouterAnswer(cmd, result.getDetails(), true); } + private Answer execute(DiagnosticsCommand cmd) { + _eachTimeout = Duration.standardSeconds(NumbersUtil.parseInt("60", 60)); + final ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), VRScripts.DIAGNOSTICS, cmd.getSrciptArguments(), _eachTimeout); + if (!result.isSuccess()) { + return new DiagnosticsAnswer(cmd, false, result.getDetails()); + } + return new DiagnosticsAnswer(cmd, result.isSuccess(), result.getDetails()); + } + private Answer execute(GetDomRVersionCmd cmd) { final ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), VRScripts.VERSION, null); if (!result.isSuccess()) { @@ -454,6 +468,6 @@ private Answer execute(AggregationControlCommand cmd) { _vrAggregateCommandsSet.remove(routerName); } } - return new Answer(cmd, false, "Fail to recongize aggregation action " + action.toString()); + return new Answer(cmd, false, "Fail to recognize aggregation action " + action.toString()); } } diff --git a/core/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsAnswer.java b/core/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsAnswer.java new file mode 100644 index 000000000000..006f0434ab37 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsAnswer.java @@ -0,0 +1,54 @@ +// 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 org.apache.cloudstack.diagnostics; + +import com.cloud.agent.api.Answer; +import com.cloud.utils.exception.CloudRuntimeException; +import com.google.common.base.Strings; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.log4j.Logger; + +import java.util.HashMap; +import java.util.Map; + +public class DiagnosticsAnswer extends Answer { + public static final Logger LOGGER = Logger.getLogger(DiagnosticsAnswer.class); + + public DiagnosticsAnswer(DiagnosticsCommand cmd, boolean result, String details) { + super(cmd, result, details); + } + + public Map getExecutionDetails() { + final Map executionDetailsMap = new HashMap<>(); + if (result == true && !Strings.isNullOrEmpty(details)) { + final String[] parseDetails = details.split("&&"); + if (parseDetails.length >= 3) { + executionDetailsMap.put(ApiConstants.STDOUT, parseDetails[0].trim()); + executionDetailsMap.put(ApiConstants.STDERR, parseDetails[1].trim()); + executionDetailsMap.put(ApiConstants.EXITCODE, String.valueOf(parseDetails[2]).trim()); + } else { + throw new CloudRuntimeException("Unsupported diagnostics command type supplied"); + } + } else { + executionDetailsMap.put(ApiConstants.STDOUT, ""); + executionDetailsMap.put(ApiConstants.STDERR, details); + executionDetailsMap.put(ApiConstants.EXITCODE, "-1"); + } + return executionDetailsMap; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsCommand.java b/core/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsCommand.java new file mode 100644 index 000000000000..14d9da9ab17d --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsCommand.java @@ -0,0 +1,44 @@ +// 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 org.apache.cloudstack.diagnostics; + +import com.cloud.agent.api.routing.NetworkElementCommand; + +public class DiagnosticsCommand extends NetworkElementCommand { + + private final String scriptArguments; + private final boolean executeInSequence; + + public DiagnosticsCommand(String scriptArguments, boolean executeInSequence) { + this.scriptArguments = scriptArguments; + this.executeInSequence = executeInSequence; + } + + public String getSrciptArguments() { + return scriptArguments; + } + + @Override + public boolean isQuery() { + return true; + } + + @Override + public boolean executeInSequence() { + return this.executeInSequence; + } +} \ No newline at end of file diff --git a/developer/pom.xml b/developer/pom.xml index 3f6236fd6903..97ec9ed1b0f7 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -22,12 +22,12 @@ - commons-dbcp - commons-dbcp + org.apache.commons + commons-dbcp2 - commons-pool - commons-pool + org.apache.commons + commons-pool2 org.jasypt diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index 38822cba16ba..d0cced3007a3 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -367,10 +367,10 @@ public void reconnect(final long hostId) throws CloudRuntimeException, AgentUnav Boolean result = propagateAgentEvent(hostId, Event.ShutdownRequested); if (result == null) { super.reconnect(hostId); - + return; } if (!result) { - throw new CloudRuntimeException("Failed to propagating agent change request event:" + Event.ShutdownRequested + " to host:" + hostId); + throw new CloudRuntimeException("Failed to propagate agent change request event:" + Event.ShutdownRequested + " to host:" + hostId); } } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 542cb4ecad7a..8c86b1570380 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -17,7 +17,9 @@ package com.cloud.vm; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URLDecoder; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -48,7 +50,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.ca.Certificate; -import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -132,7 +133,6 @@ import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.deploy.DeploymentPlanningManager; -import com.cloud.domain.dao.DomainDao; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; import com.cloud.exception.AffinityConflictException; @@ -147,7 +147,6 @@ import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; -import com.cloud.gpu.dao.VGPUTypesDao; import com.cloud.ha.HighAvailabilityManager; import com.cloud.ha.HighAvailabilityManager.WorkType; import com.cloud.host.Host; @@ -162,7 +161,6 @@ import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.router.VirtualRouter; -import com.cloud.network.rules.RulesManager; import com.cloud.offering.DiskOffering; import com.cloud.offering.DiskOfferingInfo; import com.cloud.offering.ServiceOffering; @@ -230,121 +228,93 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private static final String VM_SYNC_ALERT_SUBJECT = "VM state sync alert"; @Inject - DataStoreManager dataStoreMgr; + private DataStoreManager dataStoreMgr; @Inject - protected NetworkOrchestrationService _networkMgr; + private NetworkOrchestrationService _networkMgr; @Inject - protected NetworkModel _networkModel; + private NetworkModel _networkModel; @Inject - protected AgentManager _agentMgr; + private AgentManager _agentMgr; @Inject - protected VMInstanceDao _vmDao; + private VMInstanceDao _vmDao; @Inject - protected ServiceOfferingDao _offeringDao; + private ServiceOfferingDao _offeringDao; @Inject - protected DiskOfferingDao _diskOfferingDao; + private DiskOfferingDao _diskOfferingDao; @Inject - protected VMTemplateDao _templateDao; + private VMTemplateDao _templateDao; @Inject - protected DomainDao _domainDao; + private ItWorkDao _workDao; @Inject - protected ItWorkDao _workDao; + private UserVmDao _userVmDao; @Inject - protected UserVmDao _userVmDao; + private UserVmService _userVmService; @Inject - protected UserVmService _userVmService; + private CapacityManager _capacityMgr; @Inject - protected CapacityManager _capacityMgr; + private NicDao _nicsDao; @Inject - protected NicDao _nicsDao; + private HostDao _hostDao; @Inject - protected HostDao _hostDao; + private AlertManager _alertMgr; @Inject - protected AlertManager _alertMgr; + private GuestOSCategoryDao _guestOsCategoryDao; @Inject - protected GuestOSCategoryDao _guestOsCategoryDao; + private GuestOSDao _guestOsDao; @Inject - protected GuestOSDao _guestOsDao; + private VolumeDao _volsDao; @Inject - protected VolumeDao _volsDao; + private HighAvailabilityManager _haMgr; @Inject - protected HighAvailabilityManager _haMgr; + private HostPodDao _podDao; @Inject - protected HostPodDao _podDao; + private DataCenterDao _dcDao; @Inject - protected DataCenterDao _dcDao; + private ClusterDao _clusterDao; @Inject - protected ClusterDao _clusterDao; + private PrimaryDataStoreDao _storagePoolDao; @Inject - protected PrimaryDataStoreDao _storagePoolDao; + private HypervisorGuruManager _hvGuruMgr; @Inject - protected HypervisorGuruManager _hvGuruMgr; + private NetworkDao _networkDao; @Inject - protected NetworkDao _networkDao; + private StoragePoolHostDao _poolHostDao; @Inject - protected StoragePoolHostDao _poolHostDao; + private VMSnapshotDao _vmSnapshotDao; @Inject - protected VMSnapshotDao _vmSnapshotDao; + private AffinityGroupVMMapDao _affinityGroupVMMapDao; @Inject - protected RulesManager rulesMgr; + private EntityManager _entityMgr; @Inject - protected AffinityGroupVMMapDao _affinityGroupVMMapDao; + private GuestOSCategoryDao _guestOSCategoryDao; @Inject - protected VGPUTypesDao _vgpuTypesDao; + private GuestOSDao _guestOSDao; @Inject - protected EntityManager _entityMgr; + private ServiceOfferingDao _serviceOfferingDao; @Inject - protected GuestOSCategoryDao _guestOSCategoryDao; + private CAManager caManager; @Inject - protected GuestOSDao _guestOSDao = null; + private ResourceManager _resourceMgr; @Inject - protected UserVmDetailsDao _vmDetailsDao; + private VMSnapshotManager _vmSnapshotMgr; @Inject - protected ServiceOfferingDao _serviceOfferingDao = null; + private ClusterDetailsDao _clusterDetailsDao; @Inject - protected CAManager caManager; - - @Inject - ConfigDepot _configDepot; - - protected List hostAllocators; - - public List getHostAllocators() { - return hostAllocators; - } - - public void setHostAllocators(final List hostAllocators) { - this.hostAllocators = hostAllocators; - } - - protected List _storagePoolAllocators; - - @Inject - protected ResourceManager _resourceMgr; - - @Inject - protected VMSnapshotManager _vmSnapshotMgr = null; - @Inject - protected ClusterDetailsDao _clusterDetailsDao; - @Inject - protected UserVmDetailsDao _uservmDetailsDao; - + private UserVmDetailsDao userVmDetailsDao; @Inject - protected ConfigurationDao _configDao; + private ConfigurationDao _configDao; @Inject - VolumeOrchestrationService volumeMgr; - + private VolumeOrchestrationService volumeMgr; @Inject - DeploymentPlanningManager _dpMgr; - + private DeploymentPlanningManager _dpMgr; @Inject - protected MessageBus _messageBus; + private MessageBus _messageBus; @Inject - protected VirtualMachinePowerStateSync _syncMgr; + private VirtualMachinePowerStateSync _syncMgr; @Inject - protected VmWorkJobDao _workJobDao; + private VmWorkJobDao _workJobDao; @Inject - protected AsyncJobManager _jobMgr; + private AsyncJobManager _jobMgr; VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); @@ -387,7 +357,19 @@ public void setHostAllocators(final List hostAllocators) { ScheduledExecutorService _executor = null; - protected long _nodeId; + private long _nodeId; + + private List _storagePoolAllocators; + + private List hostAllocators; + + public List getHostAllocators() { + return hostAllocators; + } + + public void setHostAllocators(final List hostAllocators) { + this.hostAllocators = hostAllocators; + } @Override public void registerGuru(final VirtualMachine.Type type, final VirtualMachineGuru guru) { @@ -578,8 +560,8 @@ protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableExcepti final VirtualMachineGuru guru = getVmGuru(vm); guru.finalizeExpunge(vm); - //remove the overcommit detials from the uservm details - _uservmDetailsDao.removeDetails(vm.getId()); + //remove the overcommit details from the uservm details + userVmDetailsDao.removeDetails(vm.getId()); // send hypervisor-dependent commands before removing final List finalizeExpungeCommands = hvGuru.finalizeExpunge(vm); @@ -1087,13 +1069,13 @@ public void orchestrateStart(final String vmUuid, final Map 1f || Float.parseFloat(cluster_detail_ram.getValue()) > 1f)) { - _uservmDetailsDao.addDetail(vm.getId(), "cpuOvercommitRatio", cluster_detail_cpu.getValue(), true); - _uservmDetailsDao.addDetail(vm.getId(), "memoryOvercommitRatio", cluster_detail_ram.getValue(), true); - } else if (_uservmDetailsDao.findDetail(vm.getId(), "cpuOvercommitRatio") != null) { - _uservmDetailsDao.addDetail(vm.getId(), "cpuOvercommitRatio", cluster_detail_cpu.getValue(), true); - _uservmDetailsDao.addDetail(vm.getId(), "memoryOvercommitRatio", cluster_detail_ram.getValue(), true); + userVmDetailsDao.addDetail(vm.getId(), "cpuOvercommitRatio", cluster_detail_cpu.getValue(), true); + userVmDetailsDao.addDetail(vm.getId(), "memoryOvercommitRatio", cluster_detail_ram.getValue(), true); + } else if (userVmDetailsDao.findDetail(vm.getId(), "cpuOvercommitRatio") != null) { + userVmDetailsDao.addDetail(vm.getId(), "cpuOvercommitRatio", cluster_detail_cpu.getValue(), true); + userVmDetailsDao.addDetail(vm.getId(), "memoryOvercommitRatio", cluster_detail_ram.getValue(), true); } vmProfile.setCpuOvercommitRatio(Float.parseFloat(cluster_detail_cpu.getValue())); @@ -1124,6 +1106,19 @@ public void orchestrateStart(final String vmUuid, final Map vmData = null; if (defaultNic != null) { UserVmVO userVm = _userVmDao.findById(vm.getId()); - Map details = _vmDetailsDao.listDetailsKeyPairs(vm.getId()); + Map details = userVmDetailsDao.listDetailsKeyPairs(vm.getId()); userVm.setDetails(details); Network network = _networkModel.getNetwork(defaultNic.getNetworkId()); diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index f33cc9699ebe..c37955068c83 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -74,7 +74,7 @@ public class VirtualMachineManagerImplTest { @Before public void setup() { - virtualMachineManagerImpl.hostAllocators = new ArrayList<>(); + virtualMachineManagerImpl.setHostAllocators(new ArrayList<>()); when(vmInstanceMock.getId()).thenReturn(1L); when(vmInstanceMock.getServiceOfferingId()).thenReturn(2L); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java index 2509d6972c3f..2281e1d2399e 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java @@ -230,5 +230,6 @@ public static void main(String[] args) { } finally { txn.close(); } + System.exit(0); } } diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java index 45e951b59a10..8efaebe1c84f 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java @@ -1654,18 +1654,18 @@ private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String details.put(ModifyTargetsCommand.STORAGE_HOST, storagePool.getHostAddress()); details.put(ModifyTargetsCommand.STORAGE_PORT, String.valueOf(storagePool.getPort())); - ModifyTargetsCommand modifyTargetsCommand = new ModifyTargetsCommand(); + ModifyTargetsCommand cmd = new ModifyTargetsCommand(); List> targets = new ArrayList<>(); targets.add(details); - modifyTargetsCommand.setTargets(targets); - modifyTargetsCommand.setApplyToAllHostsInCluster(true); - modifyTargetsCommand.setAdd(add); - modifyTargetsCommand.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC); + cmd.setTargets(targets); + cmd.setApplyToAllHostsInCluster(true); + cmd.setAdd(add); + cmd.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC); - return modifyTargetsCommand; + return cmd; } private List sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { diff --git a/framework/db/pom.xml b/framework/db/pom.xml index 6483f4f9b037..7bf184c6d38e 100644 --- a/framework/db/pom.xml +++ b/framework/db/pom.xml @@ -28,8 +28,8 @@ javax.persistence - commons-dbcp - commons-dbcp + org.apache.commons + commons-dbcp2 commons-io @@ -37,8 +37,8 @@ ${cs.commons-io.version} - commons-pool - commons-pool + org.apache.commons + commons-pool2 org.apache.cloudstack diff --git a/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java b/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java index 6a422d30fc36..6777077c3d17 100644 --- a/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java +++ b/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java @@ -33,13 +33,14 @@ import javax.sql.DataSource; -import org.apache.commons.dbcp.ConnectionFactory; -import org.apache.commons.dbcp.DriverManagerConnectionFactory; -import org.apache.commons.dbcp.PoolableConnectionFactory; -import org.apache.commons.dbcp.PoolingDataSource; -import org.apache.commons.pool.KeyedObjectPoolFactory; -import org.apache.commons.pool.impl.GenericObjectPool; -import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory; +import org.apache.commons.dbcp2.ConnectionFactory; +import org.apache.commons.dbcp2.DriverManagerConnectionFactory; +import org.apache.commons.dbcp2.PoolableConnection; +import org.apache.commons.dbcp2.PoolableConnectionFactory; +import org.apache.commons.dbcp2.PoolingDataSource; +import org.apache.commons.pool2.ObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.apache.log4j.Logger; import com.cloud.utils.Pair; @@ -1079,24 +1080,15 @@ public static void initDataSource(Properties dbProps) { System.setProperty("javax.net.ssl.trustStorePassword", dbProps.getProperty("db.cloud.trustStorePassword")); } - final GenericObjectPool cloudConnectionPool = - new GenericObjectPool(null, cloudMaxActive, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION, cloudMaxWait, cloudMaxIdle, cloudTestOnBorrow, false, - cloudTimeBtwEvictionRunsMillis, 1, cloudMinEvcitableIdleTimeMillis, cloudTestWhileIdle); - final String cloudConnectionUri = cloudDriver + "://" + cloudHost + (s_dbHAEnabled ? "," + cloudSlaves : "") + ":" + cloudPort + "/" + cloudDbName + "?autoReconnect=" + cloudAutoReconnect + (url != null ? "&" + url : "") + (useSSL ? "&useSSL=true" : "") + (s_dbHAEnabled ? "&" + cloudDbHAParams : "") + (s_dbHAEnabled ? "&loadBalanceStrategy=" + loadBalanceStrategy : ""); DriverLoader.loadDriver(cloudDriver); - final ConnectionFactory cloudConnectionFactory = new DriverManagerConnectionFactory(cloudConnectionUri, cloudUsername, cloudPassword); - - final KeyedObjectPoolFactory poolableObjFactory = (cloudPoolPreparedStatements ? new StackKeyedObjectPoolFactory() : null); - - final PoolableConnectionFactory cloudPoolableConnectionFactory = - new PoolableConnectionFactory(cloudConnectionFactory, cloudConnectionPool, poolableObjFactory, cloudValidationQuery, false, false, isolationLevel); - // Default Data Source for CloudStack - s_ds = new PoolingDataSource(cloudPoolableConnectionFactory.getPool()); + s_ds = createDataSource(cloudConnectionUri, cloudUsername, cloudPassword, cloudMaxActive, cloudMaxIdle, cloudMaxWait, + cloudTimeBtwEvictionRunsMillis, cloudMinEvcitableIdleTimeMillis, cloudTestWhileIdle, cloudTestOnBorrow, + cloudValidationQuery, isolationLevel); // Configure the usage db final int usageMaxActive = Integer.parseInt(dbProps.getProperty("db.usage.maxActive")); @@ -1111,21 +1103,15 @@ public static void initDataSource(Properties dbProps) { final boolean usageAutoReconnect = Boolean.parseBoolean(dbProps.getProperty("db.usage.autoReconnect")); final String usageUrl = dbProps.getProperty("db.usage.url.params"); - final GenericObjectPool usageConnectionPool = - new GenericObjectPool(null, usageMaxActive, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION, usageMaxWait, usageMaxIdle); - final String usageConnectionUri = usageDriver + "://" + usageHost + (s_dbHAEnabled ? "," + dbProps.getProperty("db.cloud.slaves") : "") + ":" + usagePort + "/" + usageDbName + "?autoReconnect=" + usageAutoReconnect + (usageUrl != null ? "&" + usageUrl : "") + (s_dbHAEnabled ? "&" + getDBHAParams("usage", dbProps) : "") + (s_dbHAEnabled ? "&loadBalanceStrategy=" + loadBalanceStrategy : ""); DriverLoader.loadDriver(usageDriver); - final ConnectionFactory usageConnectionFactory = new DriverManagerConnectionFactory(usageConnectionUri, usageUsername, usagePassword); - - final PoolableConnectionFactory usagePoolableConnectionFactory = - new PoolableConnectionFactory(usageConnectionFactory, usageConnectionPool, new StackKeyedObjectPoolFactory(), null, false, false); - // Data Source for usage server - s_usageDS = new PoolingDataSource(usagePoolableConnectionFactory.getPool()); + s_usageDS = createDataSource(usageConnectionUri, usageUsername, usagePassword, + usageMaxActive, usageMaxIdle, usageMaxWait, null, null, null, null, + null, isolationLevel); try { // Configure the simulator db @@ -1140,18 +1126,12 @@ public static void initDataSource(Properties dbProps) { final String simulatorDbName = dbProps.getProperty("db.simulator.name"); final boolean simulatorAutoReconnect = Boolean.parseBoolean(dbProps.getProperty("db.simulator.autoReconnect")); - final GenericObjectPool simulatorConnectionPool = - new GenericObjectPool(null, simulatorMaxActive, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION, simulatorMaxWait, simulatorMaxIdle); - final String simulatorConnectionUri = simulatorDriver + "://" + simulatorHost + ":" + simulatorPort + "/" + simulatorDbName + "?autoReconnect=" + simulatorAutoReconnect; DriverLoader.loadDriver(simulatorDriver); - final ConnectionFactory simulatorConnectionFactory = new DriverManagerConnectionFactory(simulatorConnectionUri, simulatorUsername, simulatorPassword); - - final PoolableConnectionFactory simulatorPoolableConnectionFactory = - new PoolableConnectionFactory(simulatorConnectionFactory, simulatorConnectionPool, new StackKeyedObjectPoolFactory(), null, false, false); - s_simulatorDS = new PoolingDataSource(simulatorPoolableConnectionFactory.getPool()); + s_simulatorDS = createDataSource(simulatorConnectionUri, simulatorUsername, simulatorPassword, + simulatorMaxActive, simulatorMaxIdle, simulatorMaxWait, null, null, null, null, cloudValidationQuery, isolationLevel); } catch (Exception e) { s_logger.debug("Simulator DB properties are not available. Not initializing simulator DS"); } @@ -1165,6 +1145,54 @@ public static void initDataSource(Properties dbProps) { } } + /** + * Creates a data source + */ + private static DataSource createDataSource(String uri, String username, String password, + Integer maxActive, Integer maxIdle, Long maxWait, + Long timeBtwnEvictionRuns, Long minEvictableIdleTime, + Boolean testWhileIdle, Boolean testOnBorrow, + String validationQuery, Integer isolationLevel) { + ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(uri, username, password); + PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, null); + GenericObjectPoolConfig config = createPoolConfig(maxActive, maxIdle, maxWait, timeBtwnEvictionRuns, minEvictableIdleTime, testWhileIdle, testOnBorrow); + ObjectPool connectionPool = new GenericObjectPool<>(poolableConnectionFactory, config); + poolableConnectionFactory.setPool(connectionPool); + if (validationQuery != null) { + poolableConnectionFactory.setValidationQuery(validationQuery); + } + if (isolationLevel != null) { + poolableConnectionFactory.setDefaultTransactionIsolation(isolationLevel); + } + return new PoolingDataSource<>(connectionPool); + } + + /** + * Return a GenericObjectPoolConfig configuration usable on connection pool creation + */ + private static GenericObjectPoolConfig createPoolConfig(Integer maxActive, Integer maxIdle, Long maxWait, + Long timeBtwnEvictionRuns, Long minEvictableIdleTime, + Boolean testWhileIdle, Boolean testOnBorrow) { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(maxActive); + config.setMaxIdle(maxIdle); + config.setMaxWaitMillis(maxWait); + + if (timeBtwnEvictionRuns != null) { + config.setTimeBetweenEvictionRunsMillis(timeBtwnEvictionRuns); + } + if (minEvictableIdleTime != null) { + config.setMinEvictableIdleTimeMillis(minEvictableIdleTime); + } + if (testWhileIdle != null) { + config.setTestWhileIdle(testWhileIdle); + } + if (testOnBorrow != null) { + config.setTestOnBorrow(testOnBorrow); + } + return config; + } + private static String getDBHAParams(String dbName, Properties dbProps) { StringBuilder sb = new StringBuilder(); sb.append("failOverReadOnly=" + dbProps.getProperty("db." + dbName + ".failOverReadOnly")); @@ -1178,11 +1206,10 @@ private static String getDBHAParams(String dbName, Properties dbProps) { @SuppressWarnings({"unchecked", "rawtypes"}) private static DataSource getDefaultDataSource(final String database) { - final GenericObjectPool connectionPool = new GenericObjectPool(null, 5); final ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://localhost:3306/" + database, "cloud", "cloud"); - final PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, connectionPool, null, null, false, true); - return new PoolingDataSource( - /* connectionPool */poolableConnectionFactory.getPool()); + final PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, null); + final GenericObjectPool connectionPool = new GenericObjectPool(poolableConnectionFactory); + return new PoolingDataSource(connectionPool); } /** diff --git a/packaging/build-deb.sh b/packaging/build-deb.sh index 52a168a95ba1..e8c178350c71 100755 --- a/packaging/build-deb.sh +++ b/packaging/build-deb.sh @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. -#set -e +set -e # # This script builds Debian packages for CloudStack and does @@ -100,8 +100,7 @@ while [ -n "$1" ]; do esac done -DCH=$(which dch) -if [ -z "$DCH" ] ; then +if [ -z "$(which dch)" ] ; then echo -e "dch not found, please install devscripts at first. \nDEB Build Failed" exit 1 fi @@ -110,6 +109,17 @@ NOW="$(date +%s)" PWD=$(cd $(dirname "$0") && pwd -P) cd $PWD/../ +# Fail early if working directory is NOT clean and --use-timestamp was provided +if [ "$USE_TIMESTAMP" == "true" ]; then + if [ -n "$(cd $PWD; git status -s)" ]; then + echo "Erro: You have uncommitted changes and asked for --use-timestamp to be used." + echo " --use-timestamp flag is going to temporarily change POM versions and" + echo " revert them at the end of build, and there's no way we can do partial" + echo " revert. Please commit your changes first or omit --use-timestamp flag." + exit 1 + fi +fi + VERSION=$(head -n1 debian/changelog |awk -F [\(\)] '{print $2}') DISTCODE=$(lsb_release -sc) @@ -148,4 +158,6 @@ dpkg-buildpackage -uc -us -b /bin/mv /tmp/changelog.orig debian/changelog -(cd $PWD; git reset --hard) +if [ "$USE_TIMESTAMP" == "true" ]; then + (cd $PWD; git reset --hard) +fi diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdsCmd.java similarity index 79% rename from plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java rename to plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdsCmd.java index 5c15e01a30b8..0516b8ebc450 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdsCmd.java @@ -26,16 +26,16 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeAccessGroupIdResponse; +import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeAccessGroupIdsResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager; import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; -@APICommand(name = "getSolidFireVolumeAccessGroupId", responseObject = ApiSolidFireVolumeAccessGroupIdResponse.class, description = "Get the SF Volume Access Group ID", +@APICommand(name = "getSolidFireVolumeAccessGroupIds", responseObject = ApiSolidFireVolumeAccessGroupIdsResponse.class, description = "Get the SF Volume Access Group IDs", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class GetSolidFireVolumeAccessGroupIdCmd extends BaseCmd { - private static final Logger LOGGER = Logger.getLogger(GetSolidFireVolumeAccessGroupIdCmd.class.getName()); - private static final String NAME = "getsolidfirevolumeaccessgroupidresponse"; +public class GetSolidFireVolumeAccessGroupIdsCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(GetSolidFireVolumeAccessGroupIdsCmd.class.getName()); + private static final String NAME = "getsolidfirevolumeaccessgroupidsresponse"; @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.STRING, description = "Cluster UUID", required = true) private String clusterUuid; @@ -67,14 +67,14 @@ public long getEntityOwnerId() { @Override public void execute() { - LOGGER.info("'GetSolidFireVolumeAccessGroupIdCmd.execute' method invoked"); + LOGGER.info("'GetSolidFireVolumeAccessGroupIdsCmd.execute' method invoked"); - long sfVagId = manager.getSolidFireVolumeAccessGroupId(clusterUuid, storagePoolUuid); + long[] sfVagIds = manager.getSolidFireVolumeAccessGroupIds(clusterUuid, storagePoolUuid); - ApiSolidFireVolumeAccessGroupIdResponse response = new ApiSolidFireVolumeAccessGroupIdResponse(sfVagId); + ApiSolidFireVolumeAccessGroupIdsResponse response = new ApiSolidFireVolumeAccessGroupIdsResponse(sfVagIds); response.setResponseName(getCommandName()); - response.setObjectName("apisolidfirevolumeaccessgroupid"); + response.setObjectName("apisolidfirevolumeaccessgroupids"); this.setResponseObject(response); } diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdResponse.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdsResponse.java similarity index 71% rename from plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdResponse.java rename to plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdsResponse.java index 202a7e9ebba7..a37da406362d 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdResponse.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/response/solidfire/ApiSolidFireVolumeAccessGroupIdsResponse.java @@ -22,12 +22,12 @@ import org.apache.cloudstack.api.BaseResponse; -public class ApiSolidFireVolumeAccessGroupIdResponse extends BaseResponse { - @SerializedName("solidFireVolumeAccessGroupId") - @Param(description = "SolidFire Volume Access Group Id") - private long solidFireVolumeAccessGroupId; +public class ApiSolidFireVolumeAccessGroupIdsResponse extends BaseResponse { + @SerializedName("solidFireVolumeAccessGroupIds") + @Param(description = "SolidFire Volume Access Group Ids") + private long[] solidFireVolumeAccessGroupIds; - public ApiSolidFireVolumeAccessGroupIdResponse(long sfVolumeAccessGroupId) { - solidFireVolumeAccessGroupId = sfVolumeAccessGroupId; + public ApiSolidFireVolumeAccessGroupIdsResponse(long[] sfVolumeAccessGroupIds) { + solidFireVolumeAccessGroupIds = sfVolumeAccessGroupIds; } } \ No newline at end of file diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java index 04589038d345..4adcbbe89d8d 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/api/solidfire/ApiSolidFireIntegrationTestServiceImpl.java @@ -22,7 +22,7 @@ import org.apache.cloudstack.api.command.admin.solidfire.GetPathForVolumeCmd; // import org.apache.log4j.Logger; import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireAccountIdCmd; -import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireVolumeAccessGroupIdCmd; +import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireVolumeAccessGroupIdsCmd; import org.apache.cloudstack.api.command.admin.solidfire.GetVolumeSnapshotDetailsCmd; import org.apache.cloudstack.api.command.admin.solidfire.GetVolumeiScsiNameCmd; import org.apache.cloudstack.api.command.admin.solidfire.GetSolidFireVolumeSizeCmd; @@ -38,7 +38,7 @@ public List> getCommands() { cmdList.add(GetPathForVolumeCmd.class); cmdList.add(GetSolidFireAccountIdCmd.class); - cmdList.add(GetSolidFireVolumeAccessGroupIdCmd.class); + cmdList.add(GetSolidFireVolumeAccessGroupIdsCmd.class); cmdList.add(GetVolumeiScsiNameCmd.class); cmdList.add(GetSolidFireVolumeSizeCmd.class); cmdList.add(GetVolumeSnapshotDetailsCmd.class); diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java index bdc11807efee..302a034911f1 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManager.java @@ -18,6 +18,6 @@ public interface SolidFireIntegrationTestManager { long getSolidFireAccountId(String csAccountUuid, String storagePoolUuid); - long getSolidFireVolumeAccessGroupId(String csClusterUuid, String storagePoolUuid); + long[] getSolidFireVolumeAccessGroupIds(String csClusterUuid, String storagePoolUuid); long getSolidFireVolumeSize(String volumeUuid); } diff --git a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java index 66b92281efa3..0339379d116a 100644 --- a/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java +++ b/plugins/api/solidfire-intg-test/src/main/java/org/apache/cloudstack/solidfire/SolidFireIntegrationTestManagerImpl.java @@ -16,8 +16,8 @@ // under the License. package org.apache.cloudstack.solidfire; -import com.cloud.dc.ClusterDetailsDao; -import com.cloud.dc.ClusterDetailsVO; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; @@ -26,18 +26,21 @@ import com.cloud.user.AccountDetailsDao; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; @Component public class SolidFireIntegrationTestManagerImpl implements SolidFireIntegrationTestManager { - @Inject private AccountDetailsDao accountDetailsDao; - @Inject private ClusterDetailsDao clusterDetailsDao; + @Inject private HostDao hostDao; @Inject private SolidFireIntegrationTestUtil util; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; @Inject private VolumeDao volumeDao; @Inject private VolumeDetailsDao volumeDetailsDao; @@ -48,7 +51,7 @@ public long getSolidFireAccountId(String csAccountUuid, String storagePoolUuid) AccountDetailVO accountDetail = accountDetailsDao.findDetail(csAccountId, SolidFireUtil.getAccountKey(storagePoolId)); - if (accountDetail == null){ + if (accountDetail == null) { throw new CloudRuntimeException("Unable to find SF account for storage " + storagePoolUuid + " for CS account " + csAccountUuid); } @@ -58,14 +61,35 @@ public long getSolidFireAccountId(String csAccountUuid, String storagePoolUuid) } @Override - public long getSolidFireVolumeAccessGroupId(String csClusterUuid, String storagePoolUuid) { - long csClusterId = util.getClusterIdForClusterUuid(csClusterUuid); + public long[] getSolidFireVolumeAccessGroupIds(String csClusterUuid, String storagePoolUuid) { long storagePoolId = util.getStoragePoolIdForStoragePoolUuid(storagePoolUuid); - ClusterDetailsVO clusterDetails = clusterDetailsDao.findDetail(csClusterId, SolidFireUtil.getVagKey(storagePoolId)); - String sfVagId = clusterDetails.getValue(); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); + + List sfVags = SolidFireUtil.getAllVags(sfConnection); + + long csClusterId = util.getClusterIdForClusterUuid(csClusterUuid); + List hosts = hostDao.findByClusterId(csClusterId); + + if (hosts == null) { + return new long[0]; + } + + List vagIds = new ArrayList<>(hosts.size()); + + for (HostVO host : hosts) { + String iqn = host.getStorageUrl(); + + SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVolumeAccessGroup(iqn, sfVags); + + if (sfVag != null) { + if (!vagIds.contains(sfVag.getId())) { + vagIds.add(sfVag.getId()); + } + } + } - return Long.parseLong(sfVagId); + return vagIds.stream().mapToLong(l -> l).toArray(); } @Override diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 755dbaf68f7d..ab75a74ed401 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -47,6 +47,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GenericXML; import com.cloud.resource.RequestWrapper; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -2186,6 +2187,10 @@ So if getMinSpeed() returns null we fall back to getSpeed(). vm.addComp(devices); + GenericXML genericXML = new GenericXML(); + genericXML.setExtraConfig(vmTO.getExtraConfig()); + vm.addComp(genericXML); + return vm; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index 08ece9a104ed..8990b80db59c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -1834,4 +1834,17 @@ public String toString() { vmBuilder.append("\n"); return vmBuilder.toString(); } + + public static class GenericXML { + private String extraConfig; + + public void setExtraConfig(String extraConfig) { + this.extraConfig = extraConfig; + } + + @Override + public String toString() { + return extraConfig; + } + } } diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManager.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManager.java index 3a315509b499..6a9e70763a21 100644 --- a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManager.java +++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManager.java @@ -33,6 +33,7 @@ import com.cloud.resource.AgentResourceBase; import com.cloud.simulator.MockHost; import com.cloud.utils.component.Manager; +import org.apache.cloudstack.diagnostics.DiagnosticsCommand; public interface MockAgentManager extends Manager { public static final long DEFAULT_HOST_MEM_SIZE = 8 * 1024 * 1024 * 1024L; // 8G, unit of Mbytes @@ -64,4 +65,6 @@ boolean handleSystemVMStart(long vmId, String privateIpAddress, String privateMa Answer maintain(MaintainCommand cmd); Answer checkNetworkCommand(CheckNetworkCommand cmd); + + Answer runDiagnostics(DiagnosticsCommand cmd); } diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java index 9d1e4079d42e..7af282724b22 100644 --- a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java +++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java @@ -16,29 +16,6 @@ // under the License. package com.cloud.agent.manager; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.regex.PatternSyntaxException; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.cloudstack.ca.SetupCertificateAnswer; -import org.apache.cloudstack.ca.SetupCertificateCommand; -import org.apache.cloudstack.ca.SetupKeyStoreCommand; -import org.apache.cloudstack.ca.SetupKeystoreAnswer; -import org.apache.cloudstack.context.CallContext; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CheckHealthCommand; @@ -49,6 +26,7 @@ import com.cloud.agent.api.HostStatsEntry; import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.api.commands.SimulatorAddSecondaryAgent; import com.cloud.dc.dao.HostPodDao; import com.cloud.exception.DiscoveryException; @@ -73,6 +51,29 @@ import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; +import org.apache.cloudstack.ca.SetupCertificateAnswer; +import org.apache.cloudstack.ca.SetupCertificateCommand; +import org.apache.cloudstack.ca.SetupKeyStoreCommand; +import org.apache.cloudstack.ca.SetupKeystoreAnswer; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.diagnostics.DiagnosticsAnswer; +import org.apache.cloudstack.diagnostics.DiagnosticsCommand; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.regex.PatternSyntaxException; @Component public class MockAgentManagerImpl extends ManagerBase implements MockAgentManager { @@ -481,6 +482,7 @@ public Answer setupCertificate(SetupCertificateCommand cmd) { return new SetupCertificateAnswer(true); } + @Override public boolean start() { for (Discoverer discoverer : discoverers) { @@ -520,6 +522,14 @@ public Answer checkNetworkCommand(CheckNetworkCommand cmd) { return new CheckNetworkAnswer(cmd, true, "Network Setup check by names is done"); } + @Override + public Answer runDiagnostics(final DiagnosticsCommand cmd) { + final String vmInstance = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + final String[] args = cmd.getSrciptArguments().split(" "); + final String mockAnswer = String.format("%s %s executed in %s && && 0", args[0].toUpperCase(), args[1], vmInstance); + return new DiagnosticsAnswer(cmd, true, mockAnswer); + } + public List getDiscoverers() { return discoverers; } diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/SimulatorManagerImpl.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/SimulatorManagerImpl.java index 729777312fbd..29ad3cc4969e 100644 --- a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/SimulatorManagerImpl.java +++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/SimulatorManagerImpl.java @@ -26,6 +26,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.diagnostics.DiagnosticsCommand; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -293,7 +294,9 @@ public Answer simulate(final Command cmd, final String hostGuid) { } else if (cmd instanceof PingTestCommand) { answer = _mockAgentMgr.pingTest((PingTestCommand)cmd); } else if (cmd instanceof SetupKeyStoreCommand) { - answer = _mockAgentMgr.setupKeyStore((SetupKeyStoreCommand)cmd); + answer = _mockAgentMgr.setupKeyStore((SetupKeyStoreCommand) cmd); + }else if (cmd instanceof DiagnosticsCommand) { + answer = _mockAgentMgr.runDiagnostics((DiagnosticsCommand)cmd); } else if (cmd instanceof SetupCertificateCommand) { answer = _mockAgentMgr.setupCertificate((SetupCertificateCommand)cmd); } else if (cmd instanceof PrepareForMigrationCommand) { diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index 0cea62f18fa2..82cd4ca23d38 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -145,6 +145,7 @@ public String getName() { private static final Logger s_logger = Logger.getLogger(VmwareStorageProcessor.class); private static final int DEFAULT_NFS_PORT = 2049; + private static final int SECONDS_TO_WAIT_FOR_DATASTORE = 120; private final VmwareHostService hostService; private boolean _fullCloneFlag; @@ -2602,8 +2603,7 @@ private ManagedObjectReference getVmfsDatastore(VmwareContext context, VmwareHyp } private void waitForAllHostsToSeeDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { - long secondsToWait = 120; - long endWaitTime = System.currentTimeMillis() + secondsToWait * 1000; + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; boolean isConditionMet = false; @@ -2621,7 +2621,7 @@ private void waitForAllHostsToSeeDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { int numHostsChecked = 0; - for (Pair host: lstHosts) { + for (Pair host : lstHosts) { ManagedObjectReference morHostToMatch = host.first(); HostMO hostToMatchMO = new HostMO(dsMO.getContext(), morHostToMatch); @@ -2641,8 +2641,7 @@ private boolean verifyAllHostsSeeDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { - long secondsToWait = 120; - long endWaitTime = System.currentTimeMillis() + secondsToWait * 1000; + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; boolean isConditionMet = false; @@ -2657,13 +2656,39 @@ private void waitForAllHostsToMountDatastore(List lstHosts, DatastoreMO dsMO) throws Exception { + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; + + boolean isConditionMet = false; + + while (System.currentTimeMillis() < endWaitTime && !isConditionMet) { + Thread.sleep(5000); + + isConditionMet = verifyAllHostsMountedDatastore2(lstHosts, dsMO); + } + + if (!isConditionMet) { + throw new CloudRuntimeException("Not all hosts mounted the datastore"); + } + } + private boolean verifyAllHostsMountedDatastore(List> lstHosts, DatastoreMO dsMO) throws Exception { - int numHostsChecked = 0; + List hostMOs = new ArrayList<>(lstHosts.size()); - for (Pair host: lstHosts) { + for (Pair host : lstHosts) { ManagedObjectReference morHostToMatch = host.first(); HostMO hostToMatchMO = new HostMO(dsMO.getContext(), morHostToMatch); + hostMOs.add(hostToMatchMO); + } + + return verifyAllHostsMountedDatastore2(hostMOs, dsMO); + } + + private boolean verifyAllHostsMountedDatastore2(List lstHosts, DatastoreMO dsMO) throws Exception { + int numHostsChecked = 0; + + for (HostMO hostToMatchMO : lstHosts) { List datastoreHostMounts = dsMO.getHostMounts(); for (DatastoreHostMount datastoreHostMount : datastoreHostMounts) { @@ -2753,6 +2778,16 @@ private void mountVmfsDatastore(DatastoreMO dsMO, List host : hosts) { HostMO hostMO = new HostMO(dsMO.getContext(), host.first()); + List hostMOs = new ArrayList<>(1); + + hostMOs.add(hostMO); + + mountVmfsDatastore2(dsMO, hostMOs); + } + } + + private void mountVmfsDatastore2(DatastoreMO dsMO, List hosts) throws Exception { + for (HostMO hostMO : hosts) { if (!isDatastoreMounted(dsMO, hostMO)) { HostStorageSystemMO hostStorageSystemMO = hostMO.getHostStorageSystemMO(); @@ -2760,11 +2795,15 @@ private void mountVmfsDatastore(DatastoreMO dsMO, List> currentHosts = new ArrayList<>(1); + s_logger.trace("'" + ex.getClass().getName() + "' exception thrown: " + ex.getMessage()); - currentHosts.add(host); + List currentHosts = new ArrayList<>(1); - waitForAllHostsToMountDatastore(currentHosts, dsMO); + currentHosts.add(hostMO); + + s_logger.trace("Waiting for host " + hostMO.getHostName() + " to mount datastore " + dsMO.getName()); + + waitForAllHostsToMountDatastore2(currentHosts, dsMO); } } } @@ -2772,12 +2811,29 @@ private void mountVmfsDatastore(DatastoreMO dsMO, List> hosts) throws Exception { + for (Pair host : hosts) { + HostMO hostMO = new HostMO(context, host.first()); + + List hostMOs = new ArrayList<>(1); + + hostMOs.add(hostMO); + + unmountVmfsDatastore2(context, hyperHost, datastoreName, hostMOs); + } + } + + private void unmountVmfsDatastore2(VmwareContext context, VmwareHypervisorHost hyperHost, String datastoreName, + List hosts) throws Exception { ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, datastoreName); DatastoreMO dsMO = new DatastoreMO(context, morDs); - for (Pair host : hosts) { - HostMO hostMO = new HostMO(context, host.first()); + for (HostMO hostMO : hosts) { + unmountVmfsVolume(dsMO, hostMO); + } + } + private void unmountVmfsVolume(DatastoreMO dsMO, HostMO hostMO) throws Exception { + if (isDatastoreMounted(dsMO, hostMO)) { HostStorageSystemMO hostStorageSystemMO = hostMO.getHostStorageSystemMO(); hostStorageSystemMO.unmountVmfsVolume(getDatastoreUuid(dsMO, hostMO)); @@ -2902,6 +2958,20 @@ public void handleTargets(boolean add, ModifyTargetsCommand.TargetTypeToRemove t if (rescan) { rescanAllHosts(hosts, true, false); + + List targetsToAdd = new ArrayList<>(); + + targetsToAdd.addAll(getTargets(staticTargetsForHost)); + targetsToAdd.addAll(getTargets(dynamicTargetsForHost)); + + for (HostInternetScsiHbaStaticTarget targetToAdd : targetsToAdd) { + HostDatastoreSystemMO hostDatastoreSystemMO = host.getHostDatastoreSystemMO(); + String datastoreName = waitForDatastoreName(hostDatastoreSystemMO, targetToAdd.getIScsiName()); + ManagedObjectReference morDs = hostDatastoreSystemMO.findDatastoreByName(datastoreName); + DatastoreMO datastoreMO = new DatastoreMO(host.getContext(), morDs); + + mountVmfsDatastore2(datastoreMO, hosts); + } } } catch (Exception ex) { @@ -2924,26 +2994,9 @@ public void handleTargets(boolean add, ModifyTargetsCommand.TargetTypeToRemove t if (targetsToRemove.size() > 0) { if (isRemoveAsync) { - new Thread(() -> { - try { - addRemoveInternetScsiTargetsToAllHosts(false, targetsToRemove, hosts); - - rescanAllHosts(hosts, true, false); - } catch (Exception ex) { - s_logger.warn(ex.getMessage()); - } - }).start(); + new Thread(() -> handleRemove(targetsToRemove, host, hosts)).start(); } else { - executorService.submit(new Thread(() -> { - try { - addRemoveInternetScsiTargetsToAllHosts(false, targetsToRemove, hosts); - - rescanAllHosts(hosts, true, false); - } - catch (Exception ex) { - s_logger.warn(ex.getMessage()); - } - })); + executorService.submit(new Thread(() -> handleRemove(targetsToRemove, host, hosts))); } } } @@ -2956,6 +3009,60 @@ public void handleTargets(boolean add, ModifyTargetsCommand.TargetTypeToRemove t } } + private String waitForDatastoreName(HostDatastoreSystemMO hostDatastoreSystemMO, String iqn) throws Exception { + long endWaitTime = System.currentTimeMillis() + SECONDS_TO_WAIT_FOR_DATASTORE * 1000; + + do { + String datastoreName = getDatastoreName(hostDatastoreSystemMO, iqn); + + if (datastoreName != null) { + return datastoreName; + } + + Thread.sleep(5000); + } + while (System.currentTimeMillis() < endWaitTime); + + throw new CloudRuntimeException("Could not find the datastore name"); + } + + private String getDatastoreName(HostDatastoreSystemMO hostDatastoreSystemMO, String iqn) throws Exception { + String datastoreName = "-" + iqn + "-0"; + + ManagedObjectReference morDs = hostDatastoreSystemMO.findDatastoreByName(datastoreName); + + if (morDs != null) { + return datastoreName; + } + + datastoreName = "_" + iqn + "_0"; + + morDs = hostDatastoreSystemMO.findDatastoreByName(datastoreName); + + if (morDs != null) { + return datastoreName; + } + + return null; + } + + private void handleRemove(List targetsToRemove, HostMO host, List hosts) { + try { + for (HostInternetScsiHbaStaticTarget target : targetsToRemove) { + String datastoreName = waitForDatastoreName(host.getHostDatastoreSystemMO(), target.getIScsiName()); + + unmountVmfsDatastore2(host.getContext(), host, datastoreName, hosts); + } + + addRemoveInternetScsiTargetsToAllHosts(false, targetsToRemove, hosts); + + rescanAllHosts(hosts, true, false); + } + catch (Exception ex) { + s_logger.warn(ex.getMessage()); + } + } + private void addRemoveInternetScsiTargetsToAllHosts(VmwareContext context, final boolean add, final List targets, List> hostPairs) throws Exception { List hosts = new ArrayList<>(); diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index 3600ea92e61a..c95e4c534d93 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -30,8 +30,6 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; import com.cloud.dc.ClusterVO; -import com.cloud.dc.ClusterDetailsVO; -import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -88,7 +86,6 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { private static final Logger LOGGER = Logger.getLogger(SolidFirePrimaryDataStoreDriver.class); - private static final int LOCK_TIME_IN_SECONDS = 300; private static final int LOWEST_HYPERVISOR_SNAPSHOT_RESERVE = 10; private static final long MIN_IOPS_FOR_TEMPLATE_VOLUME = 100L; private static final long MAX_IOPS_FOR_TEMPLATE_VOLUME = 20000L; @@ -102,7 +99,6 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Inject private AccountDao accountDao; @Inject private AccountDetailsDao accountDetailsDao; @Inject private ClusterDao clusterDao; - @Inject private ClusterDetailsDao clusterDetailsDao; @Inject private DataStoreManager dataStoreMgr; @Inject private HostDao hostDao; @Inject private SnapshotDao snapshotDao; @@ -147,14 +143,8 @@ public ChapInfo getChapInfo(DataObject dataObject) { return null; } - // get the VAG associated with volumeInfo's cluster, if any (ListVolumeAccessGroups) - // if the VAG exists - // update the VAG to contain all IQNs of the hosts (ModifyVolumeAccessGroup) - // if the ID of volumeInfo in not in the VAG, add it (ModifyVolumeAccessGroup) - // if the VAG doesn't exist, create it with the IQNs of the hosts and the ID of volumeInfo (CreateVolumeAccessGroup) @Override - public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) - { + public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) { Preconditions.checkArgument(dataObject != null, "'dataObject' should not be 'null'"); Preconditions.checkArgument(host != null, "'host' should not be 'null'"); Preconditions.checkArgument(dataStore != null, "'dataStore' should not be 'null'"); @@ -167,7 +157,7 @@ public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(LOCK_TIME_IN_SECONDS)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB (in grantAccess) on the following string: " + cluster.getUuid(); LOGGER.warn(errMsg); @@ -176,32 +166,11 @@ public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore } try { - ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); - - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; - List hosts = hostDao.findByClusterId(clusterId); - if (!SolidFireUtil.hostsSupport_iScsi(hosts)) { - String errMsg = "Not all hosts in the compute cluster support iSCSI."; - - LOGGER.warn(errMsg); - - throw new CloudRuntimeException(errMsg); - } - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - if (vagId != null) { - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); - - long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); - - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds); - } - else { - SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolumeId, storagePoolId, cluster.getUuid(), hosts, clusterDetailsDao); - } + SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolumeId, hosts); return true; } @@ -211,9 +180,6 @@ public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore } } - // get the VAG associated with volumeInfo's cluster, if any (ListVolumeAccessGroups) // might not exist if using CHAP - // if the VAG exists - // remove the ID of volumeInfo from the VAG (ModifyVolumeAccessGroup) @Override public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) { @@ -229,27 +195,23 @@ public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(LOCK_TIME_IN_SECONDS)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB (in revokeAccess) on the following string: " + cluster.getUuid(); - LOGGER.debug(errMsg); + LOGGER.warn(errMsg); throw new CloudRuntimeException(errMsg); } try { - ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); - - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; - - if (vagId != null) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false); + List sfVags = SolidFireUtil.getAllVags(sfConnection); - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds); + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + if (SolidFireUtil.sfVagContains(sfVag, sfVolumeId, clusterId, hostDao)) { + SolidFireUtil.removeVolumeIdsFromSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); + } } } finally { @@ -735,11 +697,7 @@ private boolean isBasicRevokeAccess(long volumeId) { private boolean getBooleanValueFromVolumeDetails(long volumeId, String name) { VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, name); - if (volumeDetail != null && volumeDetail.getValue() != null) { - return Boolean.parseBoolean(volumeDetail.getValue()); - } - - return false; + return volumeDetail != null && volumeDetail.getValue() != null && Boolean.parseBoolean(volumeDetail.getValue()); } private long getCsIdForCloning(long volumeId, String cloneOf) { @@ -755,11 +713,7 @@ private long getCsIdForCloning(long volumeId, String cloneOf) { private boolean shouldTakeSnapshot(long snapshotId) { SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotId, "takeSnapshot"); - if (snapshotDetails != null && snapshotDetails.getValue() != null) { - return Boolean.parseBoolean(snapshotDetails.getValue()); - } - - return false; + return snapshotDetails != null && snapshotDetails.getValue() != null && Boolean.parseBoolean(snapshotDetails.getValue()); } private SolidFireUtil.SolidFireVolume createClone(SolidFireUtil.SolidFireConnection sfConnection, long dataObjectId, VolumeInfo volumeInfo, long sfAccountId, diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java index 3172b1af5b4b..2ebd69a2d93e 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java @@ -49,8 +49,6 @@ import com.cloud.agent.api.DeleteStoragePoolCommand; import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.agent.api.StoragePoolInfo; -import com.cloud.dc.ClusterDetailsDao; -import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; @@ -75,23 +73,22 @@ import com.cloud.utils.exception.CloudRuntimeException; public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { - private static final Logger s_logger = Logger.getLogger(SolidFireSharedPrimaryDataStoreLifeCycle.class); - - @Inject private AccountDao _accountDao; - @Inject private AccountDetailsDao _accountDetailsDao; - @Inject private AgentManager _agentMgr; - @Inject private ClusterDao _clusterDao; - @Inject private ClusterDetailsDao _clusterDetailsDao; - @Inject private DataCenterDao _zoneDao; - @Inject private HostDao _hostDao; - @Inject private PrimaryDataStoreDao _primaryDataStoreDao; - @Inject private PrimaryDataStoreHelper _primaryDataStoreHelper; - @Inject private ResourceManager _resourceMgr; - @Inject private StorageManager _storageMgr; - @Inject private StoragePoolAutomation _storagePoolAutomation; - @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; - @Inject private StoragePoolHostDao _storagePoolHostDao; - @Inject private TemplateManager _tmpltMgr; + private static final Logger LOGGER = Logger.getLogger(SolidFireSharedPrimaryDataStoreLifeCycle.class); + + @Inject private AccountDao accountDao; + @Inject private AccountDetailsDao accountDetailsDao; + @Inject private AgentManager agentMgr; + @Inject private ClusterDao clusterDao; + @Inject private DataCenterDao zoneDao; + @Inject private HostDao hostDao; + @Inject private PrimaryDataStoreDao primaryDataStoreDao; + @Inject private PrimaryDataStoreHelper primaryDataStoreHelper; + @Inject private ResourceManager resourceMgr; + @Inject private StorageManager storageMgr; + @Inject private StoragePoolAutomation storagePoolAutomation; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; + @Inject private StoragePoolHostDao storagePoolHostDao; + @Inject private TemplateManager tmpltMgr; // invoked to add primary storage that is based on the SolidFire plug-in @Override @@ -184,7 +181,7 @@ public DataStore initialize(Map dsInfos) { lMinIops = Long.parseLong(minIops); } } catch (Exception ex) { - s_logger.info("[ignored] error getting Min IOPS: " + ex.getLocalizedMessage()); + LOGGER.info("[ignored] error getting Min IOPS: " + ex.getLocalizedMessage()); } try { @@ -194,7 +191,7 @@ public DataStore initialize(Map dsInfos) { lMaxIops = Long.parseLong(maxIops); } } catch (Exception ex) { - s_logger.info("[ignored] error getting Max IOPS: " + ex.getLocalizedMessage()); + LOGGER.info("[ignored] error getting Max IOPS: " + ex.getLocalizedMessage()); } try { @@ -204,7 +201,7 @@ public DataStore initialize(Map dsInfos) { lBurstIops = Long.parseLong(burstIops); } } catch (Exception ex) { - s_logger.info("[ignored] error getting Burst IOPS: " + ex.getLocalizedMessage()); + LOGGER.info("[ignored] error getting Burst IOPS: " + ex.getLocalizedMessage()); } if (lMinIops > lMaxIops) { @@ -266,14 +263,14 @@ public DataStore initialize(Map dsInfos) { parameters.setPath(iqn); } - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.debug(errMsg); throw new CloudRuntimeException(errMsg); } @@ -282,21 +279,21 @@ public DataStore initialize(Map dsInfos) { try { // this adds a row in the cloud.storage_pool table for this SolidFire volume - dataStore = _primaryDataStoreHelper.createPrimaryDataStore(parameters); + dataStore = primaryDataStoreHelper.createPrimaryDataStore(parameters); // now that we have a DataStore (we need the id from the DataStore instance), we can create a Volume Access Group, if need be, and // place the newly created volume in the Volume Access Group - List hosts = _hostDao.findByClusterId(clusterId); + List hosts = hostDao.findByClusterId(clusterId); - SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolume.getId(), dataStore.getId(), cluster.getUuid(), hosts, _clusterDetailsDao); + SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolume.getId(), hosts); SolidFireUtil.SolidFireAccount sfAccount = sfCreateVolume.getAccount(); Account csAccount = CallContext.current().getCallingAccount(); - SolidFireUtil.updateCsDbWithSolidFireAccountInfo(csAccount.getId(), sfAccount, dataStore.getId(), _accountDetailsDao); + SolidFireUtil.updateCsDbWithSolidFireAccountInfo(csAccount.getId(), sfAccount, dataStore.getId(), accountDetailsDao); } catch (Exception ex) { if (dataStore != null) { - _primaryDataStoreDao.expunge(dataStore.getId()); + primaryDataStoreDao.expunge(dataStore.getId()); } throw new CloudRuntimeException(ex.getMessage()); @@ -310,7 +307,7 @@ public DataStore initialize(Map dsInfos) { } private HypervisorType getHypervisorTypeForCluster(long clusterId) { - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); if (cluster == null) { throw new CloudRuntimeException("Cluster ID '" + clusterId + "' was not found in the database."); @@ -354,7 +351,7 @@ private SolidFireCreateVolume createSolidFireVolume(SolidFireUtil.SolidFireConne try { Account csAccount = CallContext.current().getCallingAccount(); long csAccountId = csAccount.getId(); - AccountVO accountVo = _accountDao.findById(csAccountId); + AccountVO accountVo = accountDao.findById(csAccountId); String sfAccountName = SolidFireUtil.getSolidFireAccountName(accountVo.getUuid(), csAccountId); @@ -386,11 +383,11 @@ public boolean attachCluster(DataStore store, ClusterScope scope) { PrimaryDataStoreInfo primaryDataStoreInfo = (PrimaryDataStoreInfo)store; // check if there is at least one host up in this cluster - List allHosts = _resourceMgr.listAllUpHosts(Host.Type.Routing, primaryDataStoreInfo.getClusterId(), + List allHosts = resourceMgr.listAllUpHosts(Host.Type.Routing, primaryDataStoreInfo.getClusterId(), primaryDataStoreInfo.getPodId(), primaryDataStoreInfo.getDataCenterId()); if (allHosts.isEmpty()) { - _primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); + primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); throw new CloudRuntimeException("No host up to associate a storage pool with in cluster " + primaryDataStoreInfo.getClusterId()); } @@ -413,23 +410,23 @@ public boolean attachCluster(DataStore store, ClusterScope scope) { for (HostVO host : allHosts) { try { - _storageMgr.connectHostToSharedPool(host.getId(), primaryDataStoreInfo.getId()); + storageMgr.connectHostToSharedPool(host.getId(), primaryDataStoreInfo.getId()); poolHosts.add(host); } catch (Exception e) { - s_logger.warn("Unable to establish a connection between " + host + " and " + primaryDataStoreInfo, e); + LOGGER.warn("Unable to establish a connection between " + host + " and " + primaryDataStoreInfo, e); } } if (poolHosts.isEmpty()) { - s_logger.warn("No host can access storage pool '" + primaryDataStoreInfo + "' on cluster '" + primaryDataStoreInfo.getClusterId() + "'."); + LOGGER.warn("No host can access storage pool '" + primaryDataStoreInfo + "' on cluster '" + primaryDataStoreInfo.getClusterId() + "'."); - _primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); + primaryDataStoreDao.expunge(primaryDataStoreInfo.getId()); throw new CloudRuntimeException("Failed to access storage pool"); } - _primaryDataStoreHelper.attachCluster(store); + primaryDataStoreHelper.attachCluster(store); return true; } @@ -444,31 +441,31 @@ private boolean createStoragePool(HostVO host, StoragePool storagePool) { Map details = new HashMap<>(); - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); details.put(CreateStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); details.put(CreateStoragePoolCommand.IQN, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); details.put(CreateStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); details.put(CreateStoragePoolCommand.STORAGE_PORT, storagePoolDetail.getValue()); cmd.setDetails(details); } - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer != null && answer.getResult()) { return true; } else { - _primaryDataStoreDao.expunge(storagePool.getId()); + primaryDataStoreDao.expunge(storagePool.getId()); final String msg; @@ -478,7 +475,7 @@ private boolean createStoragePool(HostVO host, StoragePool storagePool) { msg = "Cannot create storage pool through host '" + hostId + "' due to CreateStoragePoolCommand returns null"; } - s_logger.warn(msg); + LOGGER.warn(msg); throw new CloudRuntimeException(msg); } @@ -491,16 +488,16 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType h @Override public boolean maintain(DataStore dataStore) { - _storagePoolAutomation.maintain(dataStore); - _primaryDataStoreHelper.maintain(dataStore); + storagePoolAutomation.maintain(dataStore); + primaryDataStoreHelper.maintain(dataStore); return true; } @Override public boolean cancelMaintain(DataStore store) { - _primaryDataStoreHelper.cancelMaintain(store); - _storagePoolAutomation.cancelMaintain(store); + primaryDataStoreHelper.cancelMaintain(store); + storagePoolAutomation.cancelMaintain(store); return true; } @@ -508,7 +505,7 @@ public boolean cancelMaintain(DataStore store) { // invoked to delete primary storage that is based on the SolidFire plug-in @Override public boolean deleteDataStore(DataStore dataStore) { - List hostPoolRecords = _storagePoolHostDao.listByPoolId(dataStore.getId()); + List hostPoolRecords = storagePoolHostDao.listByPoolId(dataStore.getId()); HypervisorType hypervisorType = null; @@ -521,11 +518,11 @@ public boolean deleteDataStore(DataStore dataStore) { } StoragePool storagePool = (StoragePool)dataStore; - StoragePoolVO storagePoolVO = _primaryDataStoreDao.findById(storagePool.getId()); - List unusedTemplatesInPool = _tmpltMgr.getUnusedTemplatesInPool(storagePoolVO); + StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(storagePool.getId()); + List unusedTemplatesInPool = tmpltMgr.getUnusedTemplatesInPool(storagePoolVO); for (VMTemplateStoragePoolVO templatePoolVO : unusedTemplatesInPool) { - _tmpltMgr.evictTemplateFromStoragePool(templatePoolVO); + tmpltMgr.evictTemplateFromStoragePool(templatePoolVO); } Long clusterId = null; @@ -539,31 +536,31 @@ public boolean deleteDataStore(DataStore dataStore) { Map details = new HashMap<>(); - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME); details.put(DeleteStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN); details.put(DeleteStoragePoolCommand.IQN, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP); details.put(DeleteStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue()); - storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); + storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT); details.put(DeleteStoragePoolCommand.STORAGE_PORT, storagePoolDetail.getValue()); deleteCmd.setDetails(details); } - final Answer answer = _agentMgr.easySend(host.getHostId(), deleteCmd); + final Answer answer = agentMgr.easySend(host.getHostId(), deleteCmd); if (answer != null && answer.getResult()) { - s_logger.info("Successfully deleted storage pool using Host ID " + host.getHostId()); + LOGGER.info("Successfully deleted storage pool using Host ID " + host.getHostId()); - HostVO hostVO = _hostDao.findById(host.getHostId()); + HostVO hostVO = hostDao.findById(host.getHostId()); if (hostVO != null) { clusterId = hostVO.getClusterId(); @@ -574,29 +571,39 @@ public boolean deleteDataStore(DataStore dataStore) { } else { if (answer != null) { - s_logger.error("Failed to delete storage pool using Host ID " + host.getHostId() + ": " + answer.getResult()); + LOGGER.error("Failed to delete storage pool using Host ID " + host.getHostId() + ": " + answer.getResult()); } else { - s_logger.error("Failed to delete storage pool using Host ID " + host.getHostId()); + LOGGER.error("Failed to delete storage pool using Host ID " + host.getHostId()); } } } if (clusterId != null) { - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) { + if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.debug(errMsg); throw new CloudRuntimeException(errMsg); } try { - removeVolumeFromVag(storagePool.getId(), clusterId); + long sfVolumeId = getVolumeId(storagePool.getId()); + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); + + List sfVags = SolidFireUtil.getAllVags(sfConnection); + + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + if (SolidFireUtil.sfVagContains(sfVag, sfVolumeId, clusterId, hostDao)) { + SolidFireUtil.removeVolumeIdsFromSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); + } + } } finally { lock.unlock(); @@ -610,16 +617,16 @@ public boolean deleteDataStore(DataStore dataStore) { deleteSolidFireVolume(storagePool.getId()); - return _primaryDataStoreHelper.deletePrimaryDataStore(dataStore); + return primaryDataStoreHelper.deletePrimaryDataStore(dataStore); } private void handleTargetsForVMware(long hostId, long storagePoolId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); if (host.getHypervisorType() == HypervisorType.VMware) { - String storageAddress = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_VIP).getValue(); - int storagePort = Integer.parseInt(_storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_PORT).getValue()); - String iqn = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.IQN).getValue(); + String storageAddress = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_VIP).getValue(); + int storagePort = Integer.parseInt(storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_PORT).getValue()); + String iqn = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.IQN).getValue(); ModifyTargetsCommand cmd = new ModifyTargetsCommand(); @@ -644,39 +651,22 @@ private void handleTargetsForVMware(long hostId, long storagePoolId) { } private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer == null) { String msg = "Unable to get an answer to the modify targets command"; - s_logger.warn(msg); + LOGGER.warn(msg); } else if (!answer.getResult()) { String msg = "Unable to modify target on the following host: " + hostId; - s_logger.warn(msg); - } - } - - private void removeVolumeFromVag(long storagePoolId, long clusterId) { - long sfVolumeId = getVolumeId(storagePoolId); - ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); - - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; - - if (vagId != null) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); - - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); - - long[] volumeIds = SolidFireUtil.getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false); - - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds); + LOGGER.warn(msg); } } private void deleteSolidFireVolume(long storagePoolId) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); long sfVolumeId = getVolumeId(storagePoolId); @@ -684,7 +674,7 @@ private void deleteSolidFireVolume(long storagePoolId) { } private long getVolumeId(long storagePoolId) { - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.VOLUME_ID); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.VOLUME_ID); String volumeId = storagePoolDetail.getValue(); @@ -692,7 +682,7 @@ private long getVolumeId(long storagePoolId) { } private long getIopsValue(long storagePoolId, String iopsKey) { - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, iopsKey); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, iopsKey); String iops = storagePoolDetail.getValue(); @@ -704,7 +694,7 @@ private static boolean isSupportedHypervisorType(HypervisorType hypervisorType) } private HypervisorType getHypervisorType(long hostId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); if (host != null) { return host.getHypervisorType(); @@ -729,7 +719,7 @@ public void updateStoragePool(StoragePool storagePool, Map detai Long capacityBytes = strCapacityBytes != null ? Long.parseLong(strCapacityBytes) : null; Long capacityIops = strCapacityIops != null ? Long.parseLong(strCapacityIops) : null; - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); long size = capacityBytes != null ? capacityBytes : storagePool.getCapacityBytes(); @@ -764,16 +754,16 @@ public void updateStoragePool(StoragePool storagePool, Map detai SolidFireUtil.modifyVolume(sfConnection, getVolumeId(storagePool.getId()), size, null, minIops, maxIops, burstIops); - SolidFireUtil.updateCsDbWithSolidFireIopsInfo(storagePool.getId(), _primaryDataStoreDao, _storagePoolDetailsDao, minIops, maxIops, burstIops); + SolidFireUtil.updateCsDbWithSolidFireIopsInfo(storagePool.getId(), primaryDataStoreDao, storagePoolDetailsDao, minIops, maxIops, burstIops); } @Override public void enableStoragePool(DataStore dataStore) { - _primaryDataStoreHelper.enable(dataStore); + primaryDataStoreHelper.enable(dataStore); } @Override public void disableStoragePool(DataStore dataStore) { - _primaryDataStoreHelper.disable(dataStore); + primaryDataStoreHelper.disable(dataStore); } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java index f2a4b79cf788..4fffb7022747 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireHostListener.java @@ -40,7 +40,6 @@ import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.alert.AlertManager; -import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; @@ -56,31 +55,31 @@ import com.cloud.vm.dao.VMInstanceDao; public class SolidFireHostListener implements HypervisorHostListener { - private static final Logger s_logger = Logger.getLogger(SolidFireHostListener.class); - - @Inject private AgentManager _agentMgr; - @Inject private AlertManager _alertMgr; - @Inject private ClusterDao _clusterDao; - @Inject private ClusterDetailsDao _clusterDetailsDao; - @Inject private DataStoreManager _dataStoreMgr; - @Inject private HostDao _hostDao; - @Inject private PrimaryDataStoreDao _storagePoolDao; - @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; + private static final Logger LOGGER = Logger.getLogger(SolidFireHostListener.class); + + @Inject private AgentManager agentMgr; + @Inject private AlertManager alertMgr; + @Inject private ClusterDao clusterDao; + @Inject private DataStoreManager dataStoreMgr; + @Inject private HostDao hostDao; + @Inject private PrimaryDataStoreDao storagePoolDao; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; @Inject private StoragePoolHostDao storagePoolHostDao; - @Inject private VMInstanceDao _vmDao; - @Inject private VolumeDao _volumeDao; + @Inject private VMInstanceDao vmDao; + @Inject private VolumeDao volumeDao; @Override public boolean hostAdded(long hostId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); if (host == null) { - s_logger.error("Failed to add host by SolidFireHostListener as host was not found with id=" + hostId); + LOGGER.error("Failed to add host by SolidFireHostListener as host was not found with id = " + hostId); + return false; } - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, host.getClusterId(), true, SolidFireUtil.PROVIDER_NAME, - _clusterDao, _clusterDetailsDao, _storagePoolDao, _storagePoolDetailsDao, _hostDao); + SolidFireUtil.hostAddedToCluster(hostId, host.getClusterId(), SolidFireUtil.PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); handleVMware(host, true, ModifyTargetsCommand.TargetTypeToRemove.NEITHER); @@ -89,7 +88,7 @@ public boolean hostAdded(long hostId) { @Override public boolean hostConnect(long hostId, long storagePoolId) { - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); StoragePoolHostVO storagePoolHost = storagePoolHostDao.findByPoolHost(storagePoolId, hostId); @@ -122,25 +121,25 @@ public boolean hostDisconnected(long hostId, long storagePoolId) { @Override public boolean hostAboutToBeRemoved(long hostId) { + HostVO host = hostDao.findById(hostId); + + SolidFireUtil.hostRemovedFromCluster(hostId, host.getClusterId(), SolidFireUtil.PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); + + handleVMware(host, false, ModifyTargetsCommand.TargetTypeToRemove.BOTH); + return true; } @Override public boolean hostRemoved(long hostId, long clusterId) { - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, clusterId, false, SolidFireUtil.PROVIDER_NAME, - _clusterDao, _clusterDetailsDao, _storagePoolDao, _storagePoolDetailsDao, _hostDao); - - HostVO host = _hostDao.findById(hostId); - - handleVMware(host, false, ModifyTargetsCommand.TargetTypeToRemove.BOTH); - return true; } private void handleXenServer(long clusterId, long hostId, long storagePoolId) { List storagePaths = getStoragePaths(clusterId, storagePoolId); - StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + StoragePool storagePool = (StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); for (String storagePath : storagePaths) { ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool); @@ -153,7 +152,7 @@ private void handleXenServer(long clusterId, long hostId, long storagePoolId) { private void handleVMware(HostVO host, boolean add, ModifyTargetsCommand.TargetTypeToRemove targetTypeToRemove) { if (host != null && HypervisorType.VMware.equals(host.getHypervisorType())) { - List storagePools = _storagePoolDao.findPoolsByProvider(SolidFireUtil.PROVIDER_NAME); + List storagePools = storagePoolDao.findPoolsByProvider(SolidFireUtil.PROVIDER_NAME); if (storagePools != null && storagePools.size() > 0) { List> targets = new ArrayList<>(); @@ -169,6 +168,7 @@ private void handleVMware(HostVO host, boolean add, ModifyTargetsCommand.TargetT cmd.setTargets(targets); cmd.setAdd(add); cmd.setTargetTypeToRemove(targetTypeToRemove); + cmd.setRemoveAsync(true); sendModifyTargetsCommand(cmd, host.getId()); } @@ -176,7 +176,7 @@ private void handleVMware(HostVO host, boolean add, ModifyTargetsCommand.TargetT } private void handleKVM(long hostId, long storagePoolId) { - StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + StoragePool storagePool = (StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool); @@ -187,19 +187,19 @@ private List getStoragePaths(long clusterId, long storagePoolId) { List storagePaths = new ArrayList<>(); // If you do not pass in null for the second parameter, you only get back applicable ROOT disks. - List volumes = _volumeDao.findByPoolId(storagePoolId, null); + List volumes = volumeDao.findByPoolId(storagePoolId, null); if (volumes != null) { for (VolumeVO volume : volumes) { Long instanceId = volume.getInstanceId(); if (instanceId != null) { - VMInstanceVO vmInstance = _vmDao.findById(instanceId); + VMInstanceVO vmInstance = vmDao.findById(instanceId); Long hostIdForVm = vmInstance.getHostId() != null ? vmInstance.getHostId() : vmInstance.getLastHostId(); if (hostIdForVm != null) { - HostVO hostForVm = _hostDao.findById(hostIdForVm); + HostVO hostForVm = hostDao.findById(hostIdForVm); if (hostForVm != null && hostForVm.getClusterId().equals(clusterId)) { storagePaths.add(volume.get_iScsiName()); @@ -215,22 +215,22 @@ private List getStoragePaths(long clusterId, long storagePoolId) { private List> getTargets(long clusterId, long storagePoolId) { List> targets = new ArrayList<>(); - StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); // If you do not pass in null for the second parameter, you only get back applicable ROOT disks. - List volumes = _volumeDao.findByPoolId(storagePoolId, null); + List volumes = volumeDao.findByPoolId(storagePoolId, null); if (volumes != null) { for (VolumeVO volume : volumes) { Long instanceId = volume.getInstanceId(); if (instanceId != null) { - VMInstanceVO vmInstance = _vmDao.findById(instanceId); + VMInstanceVO vmInstance = vmDao.findById(instanceId); Long hostIdForVm = vmInstance.getHostId() != null ? vmInstance.getHostId() : vmInstance.getLastHostId(); if (hostIdForVm != null) { - HostVO hostForVm = _hostDao.findById(hostIdForVm); + HostVO hostForVm = hostDao.findById(hostIdForVm); if (hostForVm.getClusterId().equals(clusterId)) { Map details = new HashMap<>(); @@ -250,7 +250,7 @@ private List> getTargets(long clusterId, long storagePoolId) } private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer == null) { throw new CloudRuntimeException("Unable to get an answer to the modify targets command"); @@ -259,16 +259,16 @@ private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { if (!answer.getResult()) { String msg = "Unable to modify targets on the following host: " + hostId; - HostVO host = _hostDao.findById(hostId); + HostVO host = hostDao.findById(hostId); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), msg, msg); + alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), msg, msg); throw new CloudRuntimeException(msg); } } private void sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StoragePool storagePool, long hostId) { - Answer answer = _agentMgr.easySend(hostId, cmd); + Answer answer = agentMgr.easySend(hostId, cmd); if (answer == null) { throw new CloudRuntimeException("Unable to get an answer to the modify storage pool command (" + storagePool.getId() + ")"); @@ -277,7 +277,7 @@ private void sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StorageP if (!answer.getResult()) { String msg = "Unable to attach storage pool " + storagePool.getId() + " to host " + hostId; - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg); + alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, storagePool.getDataCenterId(), storagePool.getPodId(), msg, msg); throw new CloudRuntimeException("Unable to establish a connection from agent to storage pool " + storagePool.getId() + " due to " + answer.getDetails() + " (" + storagePool.getId() + ")"); @@ -285,6 +285,6 @@ private void sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StorageP assert (answer instanceof ModifyStoragePoolAnswer) : "ModifyStoragePoolAnswer expected ; Pool = " + storagePool.getId() + " Host = " + hostId; - s_logger.info("Connection established between storage pool " + storagePool + " and host + " + hostId); + LOGGER.info("Connection established between storage pool " + storagePool + " and host + " + hostId); } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java index 66aafacdbfd0..575a3020d372 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/provider/SolidFireSharedHostListener.java @@ -40,7 +40,6 @@ import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.alert.AlertManager; -import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; @@ -57,7 +56,6 @@ public class SolidFireSharedHostListener implements HypervisorHostListener { @Inject private AgentManager agentMgr; @Inject private AlertManager alertMgr; @Inject private ClusterDao clusterDao; - @Inject private ClusterDetailsDao clusterDetailsDao; @Inject private DataStoreManager dataStoreMgr; @Inject private HostDao hostDao; @Inject private PrimaryDataStoreDao storagePoolDao; @@ -69,14 +67,15 @@ public boolean hostAdded(long hostId) { HostVO host = hostDao.findById(hostId); if (host == null) { - LOGGER.error("Failed to add host by SolidFireSharedHostListener as host was not found with id=" + hostId); + LOGGER.error("Failed to add host by SolidFireSharedHostListener as host was not found with id = " + hostId); + return false; } - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, host.getClusterId(), true, SolidFireUtil.SHARED_PROVIDER_NAME, - clusterDao, clusterDetailsDao, storagePoolDao, storagePoolDetailsDao, hostDao); + SolidFireUtil.hostAddedToCluster(hostId, host.getClusterId(), SolidFireUtil.SHARED_PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); - handleVMware(hostId, true, ModifyTargetsCommand.TargetTypeToRemove.NEITHER); + handleVMware(host, true, ModifyTargetsCommand.TargetTypeToRemove.NEITHER); return true; } @@ -123,10 +122,10 @@ public boolean hostDisconnected(long hostId, long storagePoolId) { public boolean hostAboutToBeRemoved(long hostId) { HostVO host = hostDao.findById(hostId); - SolidFireUtil.hostAddedToOrRemovedFromCluster(hostId, host.getClusterId(), false, SolidFireUtil.SHARED_PROVIDER_NAME, - clusterDao, clusterDetailsDao, storagePoolDao, storagePoolDetailsDao, hostDao); + SolidFireUtil.hostRemovedFromCluster(hostId, host.getClusterId(), SolidFireUtil.SHARED_PROVIDER_NAME, + clusterDao, hostDao, storagePoolDao, storagePoolDetailsDao); - handleVMware(hostId, false, ModifyTargetsCommand.TargetTypeToRemove.BOTH); + handleVMware(host, false, ModifyTargetsCommand.TargetTypeToRemove.BOTH); return true; } @@ -136,9 +135,7 @@ public boolean hostRemoved(long hostId, long clusterId) { return true; } - private void handleVMware(long hostId, boolean add, ModifyTargetsCommand.TargetTypeToRemove targetTypeToRemove) { - HostVO host = hostDao.findById(hostId); - + private void handleVMware(HostVO host, boolean add, ModifyTargetsCommand.TargetTypeToRemove targetTypeToRemove) { if (HypervisorType.VMware.equals(host.getHypervisorType())) { List storagePools = storagePoolDao.findPoolsByProvider(SolidFireUtil.SHARED_PROVIDER_NAME); @@ -179,7 +176,7 @@ private void handleVMware(long hostId, boolean add, ModifyTargetsCommand.TargetT cmd.setTargetTypeToRemove(targetTypeToRemove); cmd.setRemoveAsync(true); - sendModifyTargetsCommand(cmd, hostId); + sendModifyTargetsCommand(cmd, host.getId()); } } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 81adf4b343ed..1f8a2885945c 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -17,13 +17,16 @@ package org.apache.cloudstack.storage.datastore.util; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.StringTokenizer; +import java.util.UUID; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; @@ -32,8 +35,6 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import com.cloud.dc.ClusterDetailsDao; -import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; @@ -44,11 +45,14 @@ import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; +import com.google.common.base.Preconditions; import com.google.common.primitives.Longs; import com.solidfire.client.ElementFactory; import com.solidfire.element.api.Account; import com.solidfire.element.api.AddAccountRequest; +import com.solidfire.element.api.AddInitiatorsToVolumeAccessGroupRequest; +import com.solidfire.element.api.AddVolumesToVolumeAccessGroupRequest; import com.solidfire.element.api.CloneVolumeRequest; import com.solidfire.element.api.CloneVolumeResult; import com.solidfire.element.api.CreateSnapshotRequest; @@ -62,9 +66,10 @@ import com.solidfire.element.api.ListSnapshotsRequest; import com.solidfire.element.api.ListVolumeAccessGroupsRequest; import com.solidfire.element.api.ListVolumesRequest; -import com.solidfire.element.api.ModifyVolumeAccessGroupRequest; import com.solidfire.element.api.ModifyVolumeRequest; import com.solidfire.element.api.QoS; +import com.solidfire.element.api.RemoveInitiatorsFromVolumeAccessGroupRequest; +import com.solidfire.element.api.RemoveVolumesFromVolumeAccessGroupRequest; import com.solidfire.element.api.RollbackToSnapshotRequest; import com.solidfire.element.api.Snapshot; import com.solidfire.element.api.SolidFireElement; @@ -75,12 +80,13 @@ import static org.apache.commons.lang.ArrayUtils.toPrimitive; public class SolidFireUtil { - private static final Logger s_logger = Logger.getLogger(SolidFireUtil.class); + private static final Logger LOGGER = Logger.getLogger(SolidFireUtil.class); public static final String PROVIDER_NAME = "SolidFire"; public static final String SHARED_PROVIDER_NAME = "SolidFireShared"; - public static final int s_lockTimeInSeconds = 300; + private static final Random RANDOM = new Random(System.nanoTime()); + public static final int LOCK_TIME_IN_SECONDS = 300; public static final String LOG_PREFIX = "SolidFire: "; @@ -127,6 +133,8 @@ public class SolidFireUtil { public static final String DATASTORE_NAME = "datastoreName"; public static final String IQN = "iqn"; + private static final String SF_CS_ACCOUNT_PREFIX = "CloudStack_"; + public static final long MIN_VOLUME_SIZE = 1000000000; public static final long MIN_IOPS_PER_VOLUME = 100; @@ -136,6 +144,9 @@ public class SolidFireUtil { private static final int DEFAULT_MANAGEMENT_PORT = 443; private static final int DEFAULT_STORAGE_PORT = 3260; + private static final int MAX_NUM_VAGS_PER_VOLUME = 4; + private static final int MAX_NUM_INITIATORS_PER_VAG = 64; + public static class SolidFireConnection { private final String _managementVip; private final int _managementPort; @@ -300,7 +311,7 @@ public static String getValue(String keyToMatch, String url, boolean throwExcept } public static String getSolidFireAccountName(String csAccountUuid, long csAccountId) { - return "CloudStack_" + csAccountUuid + "_" + csAccountId; + return SF_CS_ACCOUNT_PREFIX + csAccountUuid + "_" + csAccountId; } public static void updateCsDbWithSolidFireIopsInfo(long storagePoolId, PrimaryDataStoreDao primaryDataStoreDao, @@ -344,17 +355,72 @@ public static SolidFireAccount getAccount(SolidFireConnection sfConnection, Stri } } - public static void hostAddedToOrRemovedFromCluster(long hostId, long clusterId, boolean added, String storageProvider, - ClusterDao clusterDao, ClusterDetailsDao clusterDetailsDao, PrimaryDataStoreDao storagePoolDao, - StoragePoolDetailsDao storagePoolDetailsDao, HostDao hostDao) { + private static boolean isCloudStackOnlyVag(SolidFireConnection sfConnection, SolidFireVag sfVag) { + long[] volumeIds = sfVag.getVolumeIds(); + + if (ArrayUtils.isEmpty(volumeIds)) { + // We count this situation as being "CloudStack only" because the reason we call this method is to determine + // if we can remove a host from a VAG (we only want to allow the host to be removed from the VAG if there are + // no non-CloudStack volumes in it). + return true; + } + + List knownSfAccountsForCs = new ArrayList<>(); + + for (long volumeId : volumeIds) { + SolidFireVolume sfVolume = getVolume(sfConnection, volumeId); + long sfAccountId = sfVolume.getAccountId(); + + if (!knownSfAccountsForCs.contains(sfAccountId)) { + SolidFireAccount sfAccount = getAccountById(sfConnection, sfAccountId); + + if (sfAccount.getName().startsWith(SF_CS_ACCOUNT_PREFIX)) { + knownSfAccountsForCs.add(sfAccountId); + } + else { + return false; + } + } + } + + return true; + } + + private static boolean isStorageApplicableToZoneOrCluster(StoragePoolVO storagePoolVO, long clusterId, ClusterDao clusterDao) { + if (storagePoolVO.getClusterId() != null) { + if (storagePoolVO.getClusterId() == clusterId) { + return true; + } + } + else { + List clustersInZone = clusterDao.listByZoneId(storagePoolVO.getDataCenterId()); + + if (clustersInZone != null) { + for (ClusterVO clusterInZone : clustersInZone) { + if (clusterInZone.getId() == clusterId) { + return true; + } + } + } + } + + return false; + } + + public static void hostRemovedFromCluster(long hostId, long clusterId, String storageProvider, ClusterDao clusterDao, HostDao hostDao, + PrimaryDataStoreDao storagePoolDao, StoragePoolDetailsDao storagePoolDetailsDao) { + HostVO hostVO = hostDao.findByIdIncludingRemoved(hostId); + + Preconditions.checkArgument(hostVO != null, "Could not locate host for ID: " + hostId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(s_lockTimeInSeconds)) { + if (!lock.lock(LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.warn(errMsg); throw new CloudRuntimeException(errMsg); } @@ -366,26 +432,72 @@ public static void hostAddedToOrRemovedFromCluster(long hostId, long clusterId, List sfConnections = new ArrayList<>(); for (StoragePoolVO storagePool : storagePools) { - ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePool.getId())); + if (!isStorageApplicableToZoneOrCluster(storagePool, clusterId, clusterDao)) { + continue; + } + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); + + if (!sfConnections.contains(sfConnection)) { + sfConnections.add(sfConnection); + + List sfVags = SolidFireUtil.getAllVags(sfConnection); + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); - String vagId = clusterDetail != null ? clusterDetail.getValue() : null; + if (sfVag != null && isCloudStackOnlyVag(sfConnection, sfVag)) { + removeInitiatorsFromSolidFireVag(sfConnection, sfVag.getId(), new String[] { hostVO.getStorageUrl() }); + } + } + } + } + } + finally { + lock.unlock(); + lock.releaseRef(); + } + } + + public static void hostAddedToCluster(long hostId, long clusterId, String storageProvider, ClusterDao clusterDao, HostDao hostDao, + PrimaryDataStoreDao storagePoolDao, StoragePoolDetailsDao storagePoolDetailsDao) { + HostVO hostVO = hostDao.findById(hostId); + + Preconditions.checkArgument(hostVO != null, "Could not locate host for ID: " + hostId); + + ClusterVO cluster = clusterDao.findById(clusterId); + + GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (vagId != null) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); + if (!lock.lock(LOCK_TIME_IN_SECONDS)) { + String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); + } + + try { + List storagePools = storagePoolDao.findPoolsByProvider(storageProvider); - if (!sfConnections.contains(sfConnection)) { - sfConnections.add(sfConnection); + if (storagePools != null && storagePools.size() > 0) { + List sfConnections = new ArrayList<>(); - SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getVag(sfConnection, Long.parseLong(vagId)); + for (StoragePoolVO storagePool : storagePools) { + if (!isStorageApplicableToZoneOrCluster(storagePool, clusterId, clusterDao)) { + continue; + } - List hostsToAddOrRemove = new ArrayList<>(); - HostVO hostToAddOrRemove = hostDao.findByIdIncludingRemoved(hostId); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); - hostsToAddOrRemove.add(hostToAddOrRemove); + if (!sfConnections.contains(sfConnection)) { + sfConnections.add(sfConnection); - String[] hostIqns = SolidFireUtil.getNewHostIqns(sfVag.getInitiators(), SolidFireUtil.getIqnsFromHosts(hostsToAddOrRemove), added); + List sfVags = SolidFireUtil.getAllVags(sfConnection); + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); - SolidFireUtil.modifyVag(sfConnection, sfVag.getId(), hostIqns, sfVag.getVolumeIds()); + if (sfVag != null) { + placeVolumeIdsInVag(sfConnection, sfVags, sfVag, hostVO, hostDao); + } else { + handleVagForHost(sfConnection, sfVags, hostVO, hostDao); } } } @@ -397,50 +509,285 @@ public static void hostAddedToOrRemovedFromCluster(long hostId, long clusterId, } } - public static long placeVolumeInVolumeAccessGroup(SolidFireConnection sfConnection, long sfVolumeId, long storagePoolId, - String vagUuid, List hosts, ClusterDetailsDao clusterDetailsDao) { - if (hosts == null || hosts.isEmpty()) { - throw new CloudRuntimeException("There must be at least one host in the cluster."); + // Put the host in an existing VAG or create a new one (only create a new one if all existing VAGs are full (i.e. 64 hosts max per VAG) and if + // creating a new VAG won't exceed 4 VAGs for the computer cluster). + // If none of the hosts in the cluster are in a VAG, then leave this host out of a VAG. + // Place applicable volume IDs in VAG, if need be (account of volume starts with SF_CS_ACCOUNT_PREFIX). + private static void handleVagForHost(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, Host host, HostDao hostDao) { + List hostVOs = hostDao.findByClusterId(host.getClusterId()); + + if (hostVOs != null) { + int numVags = 0; + + Collections.shuffle(hostVOs, RANDOM); + + for (HostVO hostVO : hostVOs) { + if (hostVO.getId() != host.getId()) { + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); + + if (sfVag != null) { + numVags++; + + // A volume should be visible to all hosts that are in the same compute cluster. That being the case, you + // can use MAX_NUM_VAGS_PER_VOLUME here. This is to limit the number of VAGs being used in a compute cluster + // to MAX_NUM_VAGS_PER_VOLUME. + if (numVags > MAX_NUM_VAGS_PER_VOLUME) { + throw new CloudRuntimeException("Can support at most four volume access groups per compute cluster (>)"); + } + + if (sfVag.getInitiators().length < MAX_NUM_INITIATORS_PER_VAG) { + if (!hostSupports_iScsi(host)) { + String errMsg = "Host with ID " + host.getId() + " does not support iSCSI."; + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); + } + + addInitiatorsToSolidFireVag(sfConnection, sfVag.getId(), new String[] { host.getStorageUrl() }); + + return; + } + } + } + } + + if (numVags == MAX_NUM_VAGS_PER_VOLUME) { + throw new CloudRuntimeException("Can support at most four volume access groups per compute cluster (==)"); + } + + if (numVags > 0) { + if (!hostSupports_iScsi(host)) { + String errMsg = "Host with ID " + host.getId() + " does not support iSCSI."; + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); + } + + SolidFireUtil.createVag(sfConnection, "CloudStack-" + UUID.randomUUID().toString(), + new String[]{host.getStorageUrl()}, getVolumeIds(sfConnection, sfVags, host, hostDao)); + } } + } - long lVagId; + /** + * Make use of the volume access group (VAG) of a random host in the cluster. With this VAG, collect all of its volume IDs that are for + * volumes that are in SolidFire accounts that are for CloudStack. + */ + private static long[] getVolumeIds(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, + Host host, HostDao hostDao) { + List volumeIdsToReturn = new ArrayList<>(); - try { - lVagId = SolidFireUtil.createVag(sfConnection, "CloudStack-" + vagUuid, - SolidFireUtil.getIqnsFromHosts(hosts), new long[] { sfVolumeId }); + SolidFireVag sfVagForRandomHostInCluster = getVagForRandomHostInCluster(sfVags, host, hostDao); + + if (sfVagForRandomHostInCluster != null) { + long[] volumeIds = sfVagForRandomHostInCluster.getVolumeIds(); + + if (volumeIds != null) { + List knownSfAccountsForCs = new ArrayList<>(); + List knownSfAccountsNotForCs = new ArrayList<>(); + + for (long volumeId : volumeIds) { + SolidFireVolume sfVolume = getVolume(sfConnection, volumeId); + long sfAccountId = sfVolume.getAccountId(); + + if (knownSfAccountsForCs.contains(sfAccountId)) { + volumeIdsToReturn.add(volumeId); + } + else if (!knownSfAccountsNotForCs.contains(sfAccountId)) { + SolidFireAccount sfAccount = getAccountById(sfConnection, sfAccountId); + + if (sfAccount.getName().startsWith(SF_CS_ACCOUNT_PREFIX)) { + knownSfAccountsForCs.add(sfAccountId); + + volumeIdsToReturn.add(volumeId); + } + else { + knownSfAccountsNotForCs.add(sfAccountId); + } + } + } + } } - catch (Exception ex) { - String iqnInVagAlready1 = "Exceeded maximum number of Volume Access Groups per initiator"; - String iqnInVagAlready2 = "Exceeded maximum number of VolumeAccessGroups per Initiator"; - if (!ex.getMessage().contains(iqnInVagAlready1) && !ex.getMessage().contains(iqnInVagAlready2)) { - throw new CloudRuntimeException(ex.getMessage()); + return volumeIdsToReturn.stream().mapToLong(l -> l).toArray(); + } + + private static void placeVolumeIdsInVag(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, + SolidFireVag sfVag, Host host, HostDao hostDao) { + SolidFireVag sfVagForRandomHostInCluster = getVagForRandomHostInCluster(sfVags, host, hostDao); + + if (sfVagForRandomHostInCluster != null) { + long[] volumeIds = sfVagForRandomHostInCluster.getVolumeIds(); + + if (volumeIds != null) { + List knownSfAccountsForCs = new ArrayList<>(); + List knownSfAccountsNotForCs = new ArrayList<>(); + + List newVolumeIds = new ArrayList<>(); + + for (long volumeId : volumeIds) { + SolidFireVolume sfVolume = getVolume(sfConnection, volumeId); + long sfAccountId = sfVolume.getAccountId(); + + if (knownSfAccountsForCs.contains(sfAccountId)) { + addVolumeIdToSolidFireVag(volumeId, sfVag, newVolumeIds); + } + else if (!knownSfAccountsNotForCs.contains(sfAccountId)) { + SolidFireAccount sfAccount = getAccountById(sfConnection, sfAccountId); + + if (sfAccount.getName().startsWith(SF_CS_ACCOUNT_PREFIX)) { + knownSfAccountsForCs.add(sfAccountId); + + addVolumeIdToSolidFireVag(volumeId, sfVag, newVolumeIds); + } + else { + knownSfAccountsNotForCs.add(sfAccountId); + } + } + } + + if (newVolumeIds.size() > 0) { + addVolumeIdsToSolidFireVag(sfConnection, sfVag.getId(), newVolumeIds.toArray(new Long[0])); + } } + } + } + + private static void addVolumeIdToSolidFireVag(long volumeId, SolidFireVag sfVag, List newVolumeIds) { + List existingVolumeIds = Longs.asList(sfVag.getVolumeIds()); - // getCompatibleVag throws an exception if an existing VAG can't be located - SolidFireUtil.SolidFireVag sfVag = getCompatibleVag(sfConnection, hosts); + if (!existingVolumeIds.contains(volumeId) && !newVolumeIds.contains(volumeId)) { + newVolumeIds.add(volumeId); + } + } - lVagId = sfVag.getId(); + private static SolidFireVag getVagForRandomHostInCluster(List sfVags, Host host, HostDao hostDao) { + List hostVOs = hostDao.findByClusterId(host.getClusterId()); - long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); + if (hostVOs != null) { + Collections.shuffle(hostVOs, RANDOM); - SolidFireUtil.modifyVag(sfConnection, lVagId, sfVag.getInitiators(), volumeIds); + for (HostVO hostVO : hostVOs) { + if (hostVO.getId() != host.getId() && hostSupports_iScsi(hostVO)) { + SolidFireVag sfVag = getVolumeAccessGroup(hostVO.getStorageUrl(), sfVags); + + if (sfVag != null) { + return sfVag; + } + } + } } - ClusterDetailsVO clusterDetail = new ClusterDetailsVO(hosts.get(0).getClusterId(), getVagKey(storagePoolId), String.valueOf(lVagId)); + return null; + } + + public static void placeVolumeInVolumeAccessGroups(SolidFireConnection sfConnection, long sfVolumeId, List hosts) { + if (!SolidFireUtil.hostsSupport_iScsi(hosts)) { + String errMsg = "Not all hosts in the compute cluster support iSCSI."; + + LOGGER.warn(errMsg); - clusterDetailsDao.persist(clusterDetail); + throw new CloudRuntimeException(errMsg); + } - return lVagId; + List sfVags = SolidFireUtil.getAllVags(sfConnection); + + Map> sfVagToIqnsMap = new HashMap<>(); + + for (HostVO hostVO : hosts) { + String iqn = hostVO.getStorageUrl(); + + SolidFireUtil.SolidFireVag sfVag = getVolumeAccessGroup(iqn, sfVags); + + List iqnsInVag = sfVagToIqnsMap.computeIfAbsent(sfVag, k -> new ArrayList<>()); + + iqnsInVag.add(iqn); + } + + if (sfVagToIqnsMap.size() > MAX_NUM_VAGS_PER_VOLUME) { + throw new CloudRuntimeException("A SolidFire volume can be in at most four volume access groups simultaneously."); + } + + for (SolidFireUtil.SolidFireVag sfVag : sfVagToIqnsMap.keySet()) { + if (sfVag != null) { + if (!SolidFireUtil.isVolumeIdInSfVag(sfVolumeId, sfVag)) { + SolidFireUtil.addVolumeIdsToSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); + } + } + else { + List iqnsNotInVag = sfVagToIqnsMap.get(null); + + SolidFireUtil.createVag(sfConnection, "CloudStack-" + UUID.randomUUID().toString(), + iqnsNotInVag.toArray(new String[0]), new long[] { sfVolumeId }); + } + } + } + + public static SolidFireUtil.SolidFireVag getVolumeAccessGroup(String hostIqn, List sfVags) { + if (hostIqn == null) { + return null; + } + + hostIqn = hostIqn.toLowerCase(); + + if (sfVags != null) { + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + List lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); + + // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null + if (lstInitiators.contains(hostIqn)) { + return sfVag; + } + } + } + + return null; } - public static boolean hostsSupport_iScsi(List hosts) { + public static boolean sfVagContains(SolidFireUtil.SolidFireVag sfVag, long sfVolumeId, long clusterId, HostDao hostDao) { + if (isVolumeIdInSfVag(sfVolumeId, sfVag)) { + String[] iqns = sfVag.getInitiators(); + List hosts = hostDao.findByClusterId(clusterId); + + for (String iqn : iqns) { + for (HostVO host : hosts) { + String hostIqn = host.getStorageUrl(); + + if (iqn.equalsIgnoreCase(hostIqn)) { + return true; + } + } + } + } + + return false; + } + + private static boolean isVolumeIdInSfVag(long sfVolumeIdToCheck, SolidFireUtil.SolidFireVag sfVag) { + long[] sfVolumeIds = sfVag.getVolumeIds(); + + for (long sfVolumeId : sfVolumeIds) { + if (sfVolumeId == sfVolumeIdToCheck) { + return true; + } + } + + return false; + } + + private static boolean hostSupports_iScsi(Host host) { + return host != null && host.getStorageUrl() != null && host.getStorageUrl().trim().length() > 0 && host.getStorageUrl().startsWith("iqn"); + } + + private static boolean hostsSupport_iScsi(List hosts) { if (hosts == null || hosts.size() == 0) { return false; } for (Host host : hosts) { - if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().length() == 0 || !host.getStorageUrl().startsWith("iqn")) { + if (!hostSupports_iScsi(host)) { return false; } } @@ -448,14 +795,6 @@ public static boolean hostsSupport_iScsi(List hosts) { return true; } - public static long[] getNewVolumeIds(long[] volumeIds, long volumeIdToAddOrRemove, boolean add) { - if (add) { - return getNewVolumeIdsAdd(volumeIds, volumeIdToAddOrRemove); - } - - return getNewVolumeIdsRemove(volumeIds, volumeIdToAddOrRemove); - } - public static String getVagKey(long storagePoolId) { return "sfVolumeAccessGroup_" + storagePoolId; } @@ -851,32 +1190,43 @@ private static long createVag(SolidFireConnection sfConnection, String vagName, return getSolidFireElement(sfConnection).createVolumeAccessGroup(request).getVolumeAccessGroupID(); } - public static void modifyVag(SolidFireConnection sfConnection, long vagId, String[] iqns, long[] volumeIds) { - ModifyVolumeAccessGroupRequest request = ModifyVolumeAccessGroupRequest.builder() + private static void addInitiatorsToSolidFireVag(SolidFireConnection sfConnection, long vagId, String[] initiators) { + AddInitiatorsToVolumeAccessGroupRequest request = AddInitiatorsToVolumeAccessGroupRequest.builder() .volumeAccessGroupID(vagId) - .optionalInitiators(iqns) - .optionalVolumes(Longs.asList(volumeIds).toArray(new Long[volumeIds.length])) + .initiators(initiators) .build(); - getSolidFireElement(sfConnection).modifyVolumeAccessGroup(request); + getSolidFireElement(sfConnection).addInitiatorsToVolumeAccessGroup(request); } - public static SolidFireVag getVag(SolidFireConnection sfConnection, long vagId) - { - ListVolumeAccessGroupsRequest request = ListVolumeAccessGroupsRequest.builder() - .optionalStartVolumeAccessGroupID(vagId) - .optionalLimit(1L) + private static void removeInitiatorsFromSolidFireVag(SolidFireConnection sfConnection, long vagId, String[] initiators) { + RemoveInitiatorsFromVolumeAccessGroupRequest request = RemoveInitiatorsFromVolumeAccessGroupRequest.builder() + .volumeAccessGroupID(vagId) + .initiators(initiators) .build(); - VolumeAccessGroup vag = getSolidFireElement(sfConnection).listVolumeAccessGroups(request).getVolumeAccessGroups()[0]; + getSolidFireElement(sfConnection).removeInitiatorsFromVolumeAccessGroup(request); + } - String[] vagIqns = vag.getInitiators(); - long[] vagVolumeIds = toPrimitive(vag.getVolumes()); + private static void addVolumeIdsToSolidFireVag(SolidFireConnection sfConnection, long vagId, Long[] volumeIds) { + AddVolumesToVolumeAccessGroupRequest request = AddVolumesToVolumeAccessGroupRequest.builder() + .volumeAccessGroupID(vagId) + .volumes(volumeIds) + .build(); - return new SolidFireVag(vagId, vagIqns, vagVolumeIds); + getSolidFireElement(sfConnection).addVolumesToVolumeAccessGroup(request); } - private static List getAllVags(SolidFireConnection sfConnection) + public static void removeVolumeIdsFromSolidFireVag(SolidFireConnection sfConnection, long vagId, Long[] volumeIds) { + RemoveVolumesFromVolumeAccessGroupRequest request = RemoveVolumesFromVolumeAccessGroupRequest.builder() + .volumeAccessGroupID(vagId) + .volumes(volumeIds) + .build(); + + getSolidFireElement(sfConnection).removeVolumesFromVolumeAccessGroup(request); + } + + public static List getAllVags(SolidFireConnection sfConnection) { ListVolumeAccessGroupsRequest request = ListVolumeAccessGroupsRequest.builder().build(); @@ -980,113 +1330,6 @@ private static int getPort(String keyToMatch, String url, int defaultPortNumber) return portNumber; } - private static String[] getNewHostIqns(String[] iqns, String[] iqnsToAddOrRemove, boolean add) { - if (add) { - return getNewHostIqnsAdd(iqns, iqnsToAddOrRemove); - } - - return getNewHostIqnsRemove(iqns, iqnsToAddOrRemove); - } - - private static String[] getNewHostIqnsAdd(String[] iqns, String[] iqnsToAdd) { - List lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList(); - - if (iqnsToAdd != null) { - for (String iqnToAdd : iqnsToAdd) { - if (!lstIqns.contains(iqnToAdd)) { - lstIqns.add(iqnToAdd); - } - } - } - - return lstIqns.toArray(new String[0]); - } - - private static String[] getNewHostIqnsRemove(String[] iqns, String[] iqnsToRemove) { - List lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList(); - - if (iqnsToRemove != null) { - for (String iqnToRemove : iqnsToRemove) { - lstIqns.remove(iqnToRemove); - } - } - - return lstIqns.toArray(new String[0]); - } - - private static long[] getNewVolumeIdsAdd(long[] volumeIds, long volumeIdToAdd) { - List lstVolumeIds = new ArrayList<>(); - - if (volumeIds != null) { - for (long volumeId : volumeIds) { - lstVolumeIds.add(volumeId); - } - } - - if (lstVolumeIds.contains(volumeIdToAdd)) { - return volumeIds; - } - - lstVolumeIds.add(volumeIdToAdd); - - return toPrimitive(lstVolumeIds.toArray(new Long[lstVolumeIds.size()])); - } - - private static long[] getNewVolumeIdsRemove(long[] volumeIds, long volumeIdToRemove) { - List lstVolumeIds = new ArrayList<>(); - - if (volumeIds != null) { - for (long volumeId : volumeIds) { - lstVolumeIds.add(volumeId); - } - } - - lstVolumeIds.remove(volumeIdToRemove); - - return toPrimitive(lstVolumeIds.toArray(new Long[lstVolumeIds.size()])); - } - - private static String[] getIqnsFromHosts(List hosts) { - if (hosts == null || hosts.size() == 0) { - throw new CloudRuntimeException("There do not appear to be any hosts in this cluster."); - } - - List lstIqns = new ArrayList<>(); - - for (Host host : hosts) { - lstIqns.add(host.getStorageUrl()); - } - - return lstIqns.toArray(new String[0]); - } - - // this method takes in a collection of hosts and tries to find an existing VAG that has all of them in it - // if successful, the VAG is returned; else, a CloudRuntimeException is thrown and this issue should be corrected by an admin - private static SolidFireUtil.SolidFireVag getCompatibleVag(SolidFireConnection sfConnection, List hosts) { - List sfVags = SolidFireUtil.getAllVags(sfConnection); - - if (sfVags != null) { - List hostIqns = new ArrayList<>(); - - // where the method we're in is called, hosts should not be null - for (HostVO host : hosts) { - // where the method we're in is called, host.getStorageUrl() should not be null (it actually should start with "iqn") - hostIqns.add(host.getStorageUrl().toLowerCase()); - } - - for (SolidFireUtil.SolidFireVag sfVag : sfVags) { - List lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); - - // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null - if (lstInitiators.containsAll(hostIqns)) { - return sfVag; - } - } - } - - throw new CloudRuntimeException("Unable to locate the appropriate SolidFire Volume Access Group"); - } - private static List getStringArrayAsLowerCaseStringList(String[] aString) { List lstLowerCaseString = new ArrayList<>(); @@ -1106,10 +1349,6 @@ private static Map convertMap(Map map) { return null; } - Map convertedMap = new HashMap<>(); - - convertedMap.putAll(map); - - return convertedMap; + return new HashMap<>(map); } } diff --git a/pom.xml b/pom.xml index c6e028d76518..cbe422e20792 100644 --- a/pom.xml +++ b/pom.xml @@ -50,8 +50,8 @@ 1.2.17 1.2.17 3.2.5 - 1.4 - 1.6 + 2.2.0 + 2.4.3 1.11 1.10 1.1.1 @@ -271,13 +271,13 @@ ${cs.cglib.version} - commons-dbcp - commons-dbcp + org.apache.commons + commons-dbcp2 ${cs.dbcp.version} - commons-pool - commons-pool + org.apache.commons + commons-pool2 @@ -297,8 +297,8 @@ ${cs.commons-fileupload.version} - commons-pool - commons-pool + org.apache.commons + commons-pool2 ${cs.pool.version} diff --git a/scripts/installer/windows/client.wxs b/scripts/installer/windows/client.wxs index 609d720a67e9..8dfec67e462e 100644 --- a/scripts/installer/windows/client.wxs +++ b/scripts/installer/windows/client.wxs @@ -242,52 +242,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1889,20 +1843,6 @@ - - - - - - - - - - - - - - diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py index 13d31f4ee3f5..0471dbea2a35 100755 --- a/scripts/vm/network/security_group.py +++ b/scripts/vm/network/security_group.py @@ -61,7 +61,7 @@ def execute(cmd): try: return check_output(cmd, shell=True) except CalledProcessError as e: - logging.exception('Failed to execute: %s', e.cmd) + pass def can_bridge_firewall(privnic): @@ -1151,8 +1151,8 @@ def getvmId(vmName): def getBrfw(brname): cmd = "iptables-save |grep physdev-is-bridged |grep FORWARD |grep BF |grep '\-o' | grep -w " + brname + "|awk '{print $9}' | head -1" - brfwname = bash("-c", cmd).stdout.strip() - if brfwname == "": + brfwname = execute(cmd).strip() + if not brfwname: brfwname = "BF-" + brname return brfwname diff --git a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java index 919d7bd9d18b..4d2452d45ce5 100644 --- a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java +++ b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java @@ -23,6 +23,7 @@ import javax.inject.Inject; +import com.cloud.storage.StoragePool; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; @@ -30,6 +31,8 @@ import org.apache.cloudstack.storage.configdrive.ConfigDrive; import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder; import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -56,7 +59,6 @@ import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage; -import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.GuestOSCategoryDao; @@ -322,20 +324,11 @@ public void commitMigration(NicProfile nic, Network network, VirtualMachineProfi private DataStore findDataStore(VirtualMachineProfile profile, DeployDestination dest) { DataStore dataStore = null; if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) { - if (dest.getStorageForDisks() != null) { - for (final Volume volume : dest.getStorageForDisks().keySet()) { - if (volume.getVolumeType() == Volume.Type.ROOT) { - final StoragePool primaryPool = dest.getStorageForDisks().get(volume); - dataStore = _dataStoreMgr.getDataStore(primaryPool.getId(), DataStoreRole.Primary); - break; - } - } + if(MapUtils.isNotEmpty(dest.getStorageForDisks())) { + dataStore = getPlannedDataStore(dest, dataStore); } if (dataStore == null) { - final List volumes = _volumeDao.findByInstanceAndType(profile.getVirtualMachine().getId(), Volume.Type.ROOT); - if (volumes != null && volumes.size() > 0) { - dataStore = _dataStoreMgr.getDataStore(volumes.get(0).getPoolId(), DataStoreRole.Primary); - } + dataStore = pickExistingRootVolumeFromDataStore(profile, dataStore); } } else { dataStore = _dataStoreMgr.getImageStore(dest.getDataCenter().getId()); @@ -343,6 +336,66 @@ private DataStore findDataStore(VirtualMachineProfile profile, DeployDestination return dataStore; } + private DataStore getPlannedDataStore(DeployDestination dest, DataStore dataStore) { + for (final Volume volume : dest.getStorageForDisks().keySet()) { + if (volume.getVolumeType() == Volume.Type.ROOT) { + final StoragePool primaryPool = dest.getStorageForDisks().get(volume); + dataStore = _dataStoreMgr.getDataStore(primaryPool.getId(), DataStoreRole.Primary); + break; + } + } + return dataStore; + } + + private DataStore pickExistingRootVolumeFromDataStore(VirtualMachineProfile profile, DataStore dataStore) { + final List volumes = _volumeDao.findByInstanceAndType(profile.getVirtualMachine().getId(), Volume.Type.ROOT); + if (CollectionUtils.isNotEmpty(volumes)) { + dataStore = pickDataStoreFromVolumes(volumes); + } + return dataStore; + } + + private DataStore pickDataStoreFromVolumes(List volumes) { + DataStore dataStore = null; + for (Volume vol : volumes) { + if (doesVolumeStateCheckout(vol)) { + dataStore = _dataStoreMgr.getDataStore(vol.getPoolId(), DataStoreRole.Primary); + if (dataStore != null) { + return dataStore; + } + } + } + return dataStore; + } + + private boolean doesVolumeStateCheckout(Volume vol) { + switch (vol.getState()) { + case Allocated: + case Creating: + case Ready: + case Snapshotting: + case RevertSnapshotting: + case Resizing: + case Copying: + case Attaching: + return true; + case Migrating: + case Expunging: + case Expunged: + case Destroy: + case Destroying: + case UploadOp: + case Uploaded: + case NotUploaded: + case UploadInProgress: + case UploadError: + case UploadAbandoned: + return false; + default: + throw new IllegalArgumentException("volume has a state that does not compute: " +vol.getState()); + } + } + private Long findAgentIdForImageStore(final DataStore dataStore) throws ResourceUnavailableException { EndPoint endpoint = _ep.select(dataStore); if (endpoint == null) { diff --git a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java index 674910d8d1ea..8734ec610863 100644 --- a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLItemCmd; import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLListCmd; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; @@ -58,6 +59,7 @@ import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.ManagerBase; @@ -958,15 +960,56 @@ public NetworkACLItem moveNetworkAclRuleToNewPosition(MoveNetworkAclItemCmd move validateMoveAclRulesData(ruleBeingMoved, previousRule, nextRule); - List allAclRules = getAllAclRulesSortedByNumber(ruleBeingMoved.getAclId()); - if (previousRule == null) { - return moveRuleToTheTop(ruleBeingMoved, allAclRules); + try { + NetworkACLVO lockedAcl = _networkACLDao.acquireInLockTable(ruleBeingMoved.getAclId()); + List allAclRules = getAllAclRulesSortedByNumber(lockedAcl.getId()); + validateAclConsistency(moveNetworkAclItemCmd, lockedAcl, allAclRules); + + if (previousRule == null) { + return moveRuleToTheTop(ruleBeingMoved, allAclRules); + } + if (nextRule == null) { + return moveRuleToTheBottom(ruleBeingMoved, allAclRules); + } + return moveRuleBetweenAclRules(ruleBeingMoved, allAclRules, previousRule, nextRule); + } finally { + _networkACLDao.releaseFromLockTable(ruleBeingMoved.getAclId()); } - if (nextRule == null) { - return moveRuleToTheBottom(ruleBeingMoved, allAclRules); + } + + /** + * Validates the consistency of the ACL; the validation process is the following. + *
    + *
  • If the ACL does not have rules yet, we do not have any validation to perform; + *
  • we will check first if the user provided a consistency hash; if not, we will log a warning message informing administrators that the user is performing the call is assuming the risks of applying ACL replacement without a consistency check; + *
  • if the ACL consistency hash is entered by the user, we check if it is the same as we currently have in the database. If it is different we throw an exception. + *
+ * + * If the consistency hash sent by the user is the same as the one we get with the database data we should be safe to proceed. + */ + protected void validateAclConsistency(MoveNetworkAclItemCmd moveNetworkAclItemCmd, NetworkACLVO lockedAcl, List allAclRules) { + if (CollectionUtils.isEmpty(allAclRules)) { + s_logger.debug(String.format("No ACL rules for [id=%s, name=%s]. Therefore, there is no need for consistency validation.", lockedAcl.getUuid(), lockedAcl.getName())); + return; } + String aclConsistencyHash = moveNetworkAclItemCmd.getAclConsistencyHash(); + if (StringUtils.isBlank(aclConsistencyHash)) { + User callingUser = CallContext.current().getCallingUser(); + Account callingAccount = CallContext.current().getCallingAccount(); - return moveRuleBetweenAclRules(ruleBeingMoved, allAclRules, previousRule, nextRule); + s_logger.warn(String.format( + "User [id=%s, name=%s] from Account [id=%s, name=%s] has not entered an ACL consistency hash to execute the replacement of an ACL rule. Therefore, she/he is assuming all of the risks of procedding without this validation.", + callingUser.getUuid(), callingUser.getUsername(), callingAccount.getUuid(), callingAccount.getAccountName())); + return; + } + String aclRulesUuids = StringUtils.EMPTY; + for (NetworkACLItemVO rule : allAclRules) { + aclRulesUuids += rule.getUuid(); + } + String md5UuidsSortedByNumber = DigestUtils.md5Hex(aclRulesUuids); + if (!md5UuidsSortedByNumber.equals(aclConsistencyHash)) { + throw new InvalidParameterValueException("It seems that the access control list in the database is not in the state that you used to apply the changed. Could you try it again?"); + } } /** diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 9bff4a17295c..3fcf7618ade3 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -19,6 +19,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -2161,9 +2162,9 @@ private DiskOfferingVO retrieveAndValidateNewDiskOffering(MigrateVolumeCmd cmd) * Performs the validations required for replacing the disk offering while migrating the volume of storage. If no new disk offering is provided, we do not execute any validation. * If a disk offering is informed, we then proceed with the following checks. *
    - *
  • We check if the given volume is of ROOT type. We cannot change the disk offering of a ROOT volume. Therefore, we thrown an {@link InvalidParameterValueException}. - *
  • We the disk is being migrated to shared storage and the new disk offering is for local storage (or vice versa), we throw an {@link InvalidParameterValueException}. Bear in mind that we are validating only the new disk offering. If none is provided we can override the current disk offering. This means, placing a volume with shared disk offering in local storage and vice versa. - *
  • We then proceed checking if the tags of the new disk offerings match the tags of the target storage. If they do not match an {@link InvalidParameterValueException} is thrown. + *
  • We check if the given volume is of ROOT type. We cannot change the disk offering of a ROOT volume. Therefore, we thrown an {@link InvalidParameterValueException}; + *
  • We the disk is being migrated to shared storage and the new disk offering is for local storage (or vice versa), we throw an {@link InvalidParameterValueException}. Bear in mind that we are validating only the new disk offering. If none is provided we can override the current disk offering. This means, placing a volume with shared disk offering in local storage and vice versa; + *
  • We then proceed checking the target storage pool supports the new disk offering {@link #doesTargetStorageSupportNewDiskOffering(StoragePool, DiskOfferingVO)}. *
* * If all of the above validations pass, we check if the size of the new disk offering is different from the volume. If it is, we log a warning message. @@ -2175,10 +2176,9 @@ protected void validateConditionsToReplaceDiskOfferingOfVolume(VolumeVO volume, if ((destPool.isShared() && newDiskOffering.getUseLocalStorage()) || destPool.isLocal() && newDiskOffering.isShared()) { throw new InvalidParameterValueException("You cannot move the volume to a shared storage and assing a disk offering for local storage and vice versa."); } - String storageTags = getStoragePoolTags(destPool); - if (!StringUtils.areTagsEqual(storageTags, newDiskOffering.getTags())) { - throw new InvalidParameterValueException(String.format("Target Storage [id=%s] tags [%s] does not match new disk offering [id=%s] tags [%s].", destPool.getUuid(), storageTags, - newDiskOffering.getUuid(), newDiskOffering.getTags())); + if (!doesTargetStorageSupportNewDiskOffering(destPool, newDiskOffering)) { + throw new InvalidParameterValueException(String.format("Target Storage [id=%s] tags [%s] does not match new disk offering [id=%s] tags [%s].", destPool.getUuid(), + getStoragePoolTags(destPool), newDiskOffering.getUuid(), newDiskOffering.getTags())); } if (volume.getSize() != newDiskOffering.getDiskSize()) { DiskOfferingVO oldDiskOffering = this._diskOfferingDao.findById(volume.getDiskOfferingId()); @@ -2189,6 +2189,58 @@ protected void validateConditionsToReplaceDiskOfferingOfVolume(VolumeVO volume, s_logger.info(String.format("Changing disk offering to [uuid=%s] while migrating volume [uuid=%s, name=%s].", newDiskOffering.getUuid(), volume.getUuid(), volume.getName())); } + /** + * Checks if the target storage supports the new disk offering. + * This validation is consistent with the mechanism used to select a storage pool to deploy a volume when a virtual machine is deployed or when a new data disk is allocated. + * + * The scenarios when this method returns true or false is presented in the following table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
#Disk offering tagsStorage tagsDoes the storage support the disk offering?
1A,BANO
2A,B,CA,B,C,D,XYES
3A,B,CX,Y,ZNO
4nullA,S,DYES
5AnullNO
6nullnullYES
+ */ + protected boolean doesTargetStorageSupportNewDiskOffering(StoragePool destPool, DiskOfferingVO newDiskOffering) { + String newDiskOfferingTags = newDiskOffering.getTags(); + return doesTargetStorageSupportDiskOffering(destPool, newDiskOfferingTags); + } + + @Override + public boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags) { + if (org.apache.commons.lang.StringUtils.isBlank(diskOfferingTags)) { + return true; + } + String storagePoolTags = getStoragePoolTags(destPool); + if (org.apache.commons.lang.StringUtils.isBlank(storagePoolTags)) { + return false; + } + String[] storageTagsAsStringArray = org.apache.commons.lang.StringUtils.split(storagePoolTags, ","); + String[] newDiskOfferingTagsAsStringArray = org.apache.commons.lang.StringUtils.split(diskOfferingTags, ","); + + return CollectionUtils.isSubCollection(Arrays.asList(newDiskOfferingTagsAsStringArray), Arrays.asList(storageTagsAsStringArray)); + } + /** * Retrieves the storage pool tags as a {@link String}. If the storage pool does not have tags we return a null value. */ diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 86c87ed02b8d..fd2138b86323 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -39,6 +39,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.google.common.base.Strings; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -515,6 +516,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private static final ConfigKey AllowDeployVmIfGivenHostFails = new ConfigKey("Advanced", Boolean.class, "allow.deploy.vm.if.deploy.on.given.host.fails", "false", "allow vm to deploy on different host if vm fails to deploy on the given host ", true); + private static final ConfigKey EnableAdditionalVmConfig = new ConfigKey<>("Advanced", Boolean.class, "enable.additional.vm.configuration", "false", "allow additional arbitrary configuration to vm", true); + @Override public UserVmVO getVirtualMachine(long vmId) { @@ -4852,6 +4855,14 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE _tmplService.attachIso(tmpl.getId(), vm.getId()); } } + + // Add extraConfig to user_vm_details table + if (EnableAdditionalVmConfig.value()) { + String extraConfig = cmd.getExtraConfig(); + if (!Strings.isNullOrEmpty(extraConfig)) { + _uservmDetailsDao.addDetail(vm.getId(), "extraConfig", extraConfig, true); + } + } return vm; } @@ -6399,7 +6410,7 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { return new ConfigKey[] {EnableDynamicallyScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax, - VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails}; + VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig}; } @Override diff --git a/server/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImpl.java b/server/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImpl.java new file mode 100644 index 000000000000..a06b5bb13071 --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImpl.java @@ -0,0 +1,131 @@ +// +// 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 org.apache.cloudstack.diagnostics; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.VMInstanceDao; +import com.google.common.base.Strings; +import org.apache.cloudstack.api.command.admin.diagnostics.RunDiagnosticsCmd; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +public class DiagnosticsServiceImpl extends ManagerBase implements PluggableService, DiagnosticsService { + private static final Logger LOGGER = Logger.getLogger(DiagnosticsServiceImpl.class); + + @Inject + private AgentManager agentManager; + @Inject + private VMInstanceDao instanceDao; + @Inject + private VirtualMachineManager vmManager; + @Inject + private NetworkOrchestrationService networkManager; + + @Override + public Map runDiagnosticsCommand(final RunDiagnosticsCmd cmd) { + final Long vmId = cmd.getId(); + final String cmdType = cmd.getType().getValue(); + final String ipAddress = cmd.getAddress(); + final String optionalArguments = cmd.getOptionalArguments(); + final VMInstanceVO vmInstance = instanceDao.findByIdTypes(vmId, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.DomainRouter, VirtualMachine.Type.SecondaryStorageVm); + + if (vmInstance == null) { + throw new InvalidParameterValueException("Unable to find a system vm with id " + vmId); + } + final Long hostId = vmInstance.getHostId(); + + if (hostId == null) { + throw new CloudRuntimeException("Unable to find host for virtual machine instance: " + vmInstance.getInstanceName()); + } + + final String shellCmd = prepareShellCmd(cmdType, ipAddress, optionalArguments); + + if (Strings.isNullOrEmpty(shellCmd)) { + throw new IllegalArgumentException("Optional parameters contain unwanted characters: " + optionalArguments); + } + + final Hypervisor.HypervisorType hypervisorType = vmInstance.getHypervisorType(); + + final DiagnosticsCommand command = new DiagnosticsCommand(shellCmd, vmManager.getExecuteInSequence(hypervisorType)); + final Map accessDetails = networkManager.getSystemVMAccessDetails(vmInstance); + + if (Strings.isNullOrEmpty(accessDetails.get(NetworkElementCommand.ROUTER_IP))) { + throw new CloudRuntimeException("Unable to set system vm ControlIP for system vm with ID: " + vmId); + } + + command.setAccessDetail(accessDetails); + + Map detailsMap; + + final Answer answer = agentManager.easySend(hostId, command); + + if (answer != null && (answer instanceof DiagnosticsAnswer)) { + detailsMap = ((DiagnosticsAnswer) answer).getExecutionDetails(); + return detailsMap; + } else { + throw new CloudRuntimeException("Failed to execute diagnostics command on remote host: " + answer.getDetails()); + } + } + + protected boolean hasValidChars(String optionalArgs) { + if (Strings.isNullOrEmpty(optionalArgs)) { + return true; + } else { + final String regex = "^[\\w\\-\\s.]+$"; + final Pattern pattern = Pattern.compile(regex); + return pattern.matcher(optionalArgs).find(); + } + + } + + protected String prepareShellCmd(String cmdType, String ipAddress, String optionalParams) { + final String CMD_TEMPLATE = String.format("%s %s", cmdType, ipAddress); + if (Strings.isNullOrEmpty(optionalParams)) { + return CMD_TEMPLATE; + } else { + if (hasValidChars(optionalParams)) { + return String.format("%s %s", CMD_TEMPLATE, optionalParams); + } else { + return null; + } + } + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList<>(); + cmdList.add(RunDiagnosticsCmd.class); + return cmdList; + } +} \ No newline at end of file diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index c7715a866c4f..2f67c4248d35 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -298,4 +298,6 @@ + + diff --git a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java index 598057402f20..9815526b6aaa 100644 --- a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java @@ -56,6 +56,7 @@ import com.cloud.network.vpc.dao.NetworkACLDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import com.cloud.user.User; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; @@ -113,10 +114,15 @@ public class NetworkACLServiceImplTest { private NetworkACLItemVO nextAclRuleMock; private String nextAclRuleUuid = "uuidNextAclRule"; + @Mock + private CallContext callContextMock; + @Before public void befoteTest() { PowerMockito.mockStatic(CallContext.class); - PowerMockito.when(CallContext.current()).thenReturn(Mockito.mock(CallContext.class)); + PowerMockito.when(CallContext.current()).thenReturn(callContextMock); + Mockito.doReturn(Mockito.mock(User.class)).when(callContextMock).getCallingUser(); + Mockito.doReturn(Mockito.mock(Account.class)).when(callContextMock).getCallingAccount(); Mockito.when(networkAclDaoMock.findById(networkAclListId)).thenReturn(networkACLVOMock); Mockito.when(createNetworkAclCmdMock.getNetworkId()).thenReturn(1L); @@ -936,6 +942,8 @@ public void moveNetworkAclRuleToNewPositionTestMoveRuleToTop() { Mockito.verify(networkAclServiceImpl, Mockito.times(0)).moveRuleToTheBottom(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class)); Mockito.verify(networkAclServiceImpl, Mockito.times(0)).moveRuleBetweenAclRules(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class), Mockito.eq(previousAclRuleMock), Mockito.eq(nextAclRuleMock)); + Mockito.verify(networkAclServiceImpl, Mockito.times(1)).validateAclConsistency(Mockito.any(MoveNetworkAclItemCmd.class), Mockito.any(NetworkACLVO.class), + Mockito.anyListOf(NetworkACLItemVO.class)); } @Test @@ -957,6 +965,8 @@ public void moveNetworkAclRuleToNewPositionTestMoveRuleToBottom() { Mockito.verify(networkAclServiceImpl, Mockito.times(1)).moveRuleToTheBottom(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class)); Mockito.verify(networkAclServiceImpl, Mockito.times(0)).moveRuleBetweenAclRules(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class), Mockito.eq(previousAclRuleMock), Mockito.eq(nextAclRuleMock)); + Mockito.verify(networkAclServiceImpl, Mockito.times(1)).validateAclConsistency(Mockito.any(MoveNetworkAclItemCmd.class), Mockito.any(NetworkACLVO.class), + Mockito.anyListOf(NetworkACLItemVO.class)); } @Test @@ -978,11 +988,17 @@ public void moveNetworkAclRuleToNewPositionTestMoveBetweenAclRules() { Mockito.verify(networkAclServiceImpl, Mockito.times(0)).moveRuleToTheBottom(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class)); Mockito.verify(networkAclServiceImpl, Mockito.times(1)).moveRuleBetweenAclRules(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class), Mockito.eq(previousAclRuleMock), Mockito.eq(nextAclRuleMock)); + Mockito.verify(networkAclServiceImpl, Mockito.times(1)).validateAclConsistency(Mockito.any(MoveNetworkAclItemCmd.class), Mockito.any(NetworkACLVO.class), + Mockito.anyListOf(NetworkACLItemVO.class)); } private void configureMoveMethodsToDoNothing() { - Mockito.doReturn(new ArrayList<>()).when(networkAclServiceImpl).getAllAclRulesSortedByNumber(networkAclMockId); + Mockito.doReturn(networkACLVOMock).when(networkAclDaoMock).acquireInLockTable(Mockito.anyLong()); + Mockito.doReturn(true).when(networkAclDaoMock).releaseFromLockTable(Mockito.anyLong()); + + Mockito.doNothing().when(networkAclServiceImpl).validateAclConsistency(Mockito.any(MoveNetworkAclItemCmd.class), Mockito.any(NetworkACLVO.class), Mockito.anyListOf(NetworkACLItemVO.class)); + Mockito.doReturn(new ArrayList<>()).when(networkAclServiceImpl).getAllAclRulesSortedByNumber(networkAclMockId); Mockito.doReturn(aclRuleBeingMovedMock).when(networkAclServiceImpl).moveRuleToTheTop(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class)); Mockito.doReturn(aclRuleBeingMovedMock).when(networkAclServiceImpl).moveRuleToTheBottom(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class)); Mockito.doReturn(aclRuleBeingMovedMock).when(networkAclServiceImpl).moveRuleBetweenAclRules(Mockito.eq(aclRuleBeingMovedMock), Mockito.anyListOf(NetworkACLItemVO.class), @@ -1281,4 +1297,73 @@ public void updateAclRuleToNewPositionAndExecuteShiftIfNecessaryTest() { Assert.assertEquals(14, networkACLItemVO13.getNumber()); Assert.assertEquals(15, networkACLItemVO14.getNumber()); } + + @Test + public void validateAclConsistencyTestRuleListEmpty() { + networkAclServiceImpl.validateAclConsistency(moveNetworkAclItemCmdMock, networkACLVOMock, new ArrayList<>()); + + Mockito.verify(moveNetworkAclItemCmdMock, Mockito.times(0)).getAclConsistencyHash(); + } + + @Test + public void validateAclConsistencyTestRuleListNull() { + networkAclServiceImpl.validateAclConsistency(moveNetworkAclItemCmdMock, networkACLVOMock, null); + + Mockito.verify(moveNetworkAclItemCmdMock, Mockito.times(0)).getAclConsistencyHash(); + } + + @Test + public void validateAclConsistencyTestAclConsistencyHashIsNull() { + Mockito.doReturn(null).when(moveNetworkAclItemCmdMock).getAclConsistencyHash(); + + validateAclConsistencyTestAclConsistencyHashBlank(); + } + + @Test + public void validateAclConsistencyTestAclConsistencyHashIsEmpty() { + Mockito.doReturn("").when(moveNetworkAclItemCmdMock).getAclConsistencyHash(); + + validateAclConsistencyTestAclConsistencyHashBlank(); + } + + @Test + public void validateAclConsistencyTestAclConsistencyHashIsBlank() { + Mockito.doReturn(" ").when(moveNetworkAclItemCmdMock).getAclConsistencyHash(); + + validateAclConsistencyTestAclConsistencyHashBlank(); + } + + private void validateAclConsistencyTestAclConsistencyHashBlank() { + ArrayList allAclRules = new ArrayList<>(); + allAclRules.add(networkAclItemVoMock); + + networkAclServiceImpl.validateAclConsistency(moveNetworkAclItemCmdMock, networkACLVOMock, allAclRules); + + Mockito.verify(moveNetworkAclItemCmdMock, Mockito.times(1)).getAclConsistencyHash(); + Mockito.verify(callContextMock, Mockito.times(1)).getCallingAccount(); + Mockito.verify(callContextMock, Mockito.times(1)).getCallingUser(); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAclConsistencyTestAclConsistencyHashIsNotEqualsToDatabaseHash() { + Mockito.doReturn("differentHash").when(moveNetworkAclItemCmdMock).getAclConsistencyHash(); + + ArrayList allAclRules = new ArrayList<>(); + allAclRules.add(networkAclItemVoMock); + + networkAclServiceImpl.validateAclConsistency(moveNetworkAclItemCmdMock, networkACLVOMock, allAclRules); + } + + @Test + public void validateAclConsistencyTest() { + Mockito.doReturn("eac527fe45c77232ef06d9c7eb8abd94").when(moveNetworkAclItemCmdMock).getAclConsistencyHash(); + + ArrayList allAclRules = new ArrayList<>(); + allAclRules.add(networkAclItemVoMock); + + Mockito.doReturn("someUuid").when(networkAclItemVoMock).getUuid(); + networkAclServiceImpl.validateAclConsistency(moveNetworkAclItemCmdMock, networkACLVOMock, allAclRules); + + Mockito.verify(moveNetworkAclItemCmdMock, Mockito.times(1)).getAclConsistencyHash(); + } } \ No newline at end of file diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index 8ae122d4c461..890b1eab526d 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -613,7 +613,6 @@ public void validateConditionsToReplaceDiskOfferingOfVolumeTestEverythingWorking inOrder.verify(storagePoolMock).isLocal(); inOrder.verify(newDiskOfferingMock, times(0)).isShared(); inOrder.verify(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); - inOrder.verify(newDiskOfferingMock).getTags(); inOrder.verify(volumeVoMock).getSize(); inOrder.verify(newDiskOfferingMock).getDiskSize(); @@ -996,4 +995,95 @@ public void deleteVolumeTestVolumeStateReadyThrowingRuntimeException() throws In volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock); } + + @Test + public void doesTargetStorageSupportDiskOfferingTestDiskOfferingMoreTagsThanStorageTags() { + DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class); + Mockito.doReturn("A,B,C").when(diskOfferingVoMock).getTags(); + + StoragePool storagePoolMock = Mockito.mock(StoragePool.class); + Mockito.doReturn("A").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); + + boolean result = volumeApiServiceImpl.doesTargetStorageSupportNewDiskOffering(storagePoolMock, diskOfferingVoMock); + + Assert.assertFalse(result); + } + + @Test + public void doesTargetStorageSupportDiskOfferingTestDiskOfferingTagsIsSubSetOfStorageTags() { + DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class); + Mockito.doReturn("A,B,C").when(diskOfferingVoMock).getTags(); + + StoragePool storagePoolMock = Mockito.mock(StoragePool.class); + Mockito.doReturn("A,B,C,D,X,Y").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); + + boolean result = volumeApiServiceImpl.doesTargetStorageSupportNewDiskOffering(storagePoolMock, diskOfferingVoMock); + + Assert.assertTrue(result); + } + + @Test + public void doesTargetStorageSupportDiskOfferingTestDiskOfferingTagsEmptyAndStorageTagsNotEmpty() { + DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class); + Mockito.doReturn("").when(diskOfferingVoMock).getTags(); + + StoragePool storagePoolMock = Mockito.mock(StoragePool.class); + Mockito.doReturn("A,B,C,D,X,Y").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); + + boolean result = volumeApiServiceImpl.doesTargetStorageSupportNewDiskOffering(storagePoolMock, diskOfferingVoMock); + + Assert.assertTrue(result); + } + + @Test + public void doesTargetStorageSupportDiskOfferingTestDiskOfferingTagsNotEmptyAndStorageTagsEmpty() { + DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class); + Mockito.doReturn("A").when(diskOfferingVoMock).getTags(); + + StoragePool storagePoolMock = Mockito.mock(StoragePool.class); + Mockito.doReturn("").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); + + boolean result = volumeApiServiceImpl.doesTargetStorageSupportNewDiskOffering(storagePoolMock, diskOfferingVoMock); + + Assert.assertFalse(result); + } + + @Test + public void doesTargetStorageSupportDiskOfferingTestDiskOfferingTagsEmptyAndStorageTagsEmpty() { + DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class); + Mockito.doReturn("").when(diskOfferingVoMock).getTags(); + + StoragePool storagePoolMock = Mockito.mock(StoragePool.class); + Mockito.doReturn("").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); + + boolean result = volumeApiServiceImpl.doesTargetStorageSupportNewDiskOffering(storagePoolMock, diskOfferingVoMock); + + Assert.assertTrue(result); + } + + @Test + public void doesTargetStorageSupportDiskOfferingTestDiskOfferingTagsDifferentFromdStorageTags() { + DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class); + Mockito.doReturn("A,B").when(diskOfferingVoMock).getTags(); + + StoragePool storagePoolMock = Mockito.mock(StoragePool.class); + Mockito.doReturn("C,D").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); + + boolean result = volumeApiServiceImpl.doesTargetStorageSupportNewDiskOffering(storagePoolMock, diskOfferingVoMock); + + Assert.assertFalse(result); + } + + @Test + public void doesTargetStorageSupportDiskOfferingTestDiskOfferingTagsEqualsStorageTags() { + DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class); + Mockito.doReturn("A").when(diskOfferingVoMock).getTags(); + + StoragePool storagePoolMock = Mockito.mock(StoragePool.class); + Mockito.doReturn("A").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); + + boolean result = volumeApiServiceImpl.doesTargetStorageSupportNewDiskOffering(storagePoolMock, diskOfferingVoMock); + + Assert.assertTrue(result); + } } diff --git a/server/src/test/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImplTest.java new file mode 100644 index 000000000000..d85c5434d9a1 --- /dev/null +++ b/server/src/test/java/org/apache/cloudstack/diagnostics/DiagnosticsServiceImplTest.java @@ -0,0 +1,196 @@ +// +// 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 org.apache.cloudstack.diagnostics; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.VMInstanceDao; +import junit.framework.TestCase; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.command.admin.diagnostics.RunDiagnosticsCmd; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.HashMap; +import java.util.Map; + +@RunWith(MockitoJUnitRunner.class) +public class DiagnosticsServiceImplTest extends TestCase { + + @Mock + private AgentManager agentManager; + @Mock + private VMInstanceDao instanceDao; + @Mock + private RunDiagnosticsCmd diagnosticsCmd; + @Mock + private DiagnosticsCommand command; + @Mock + private VMInstanceVO instanceVO; + @Mock + private VirtualMachineManager vmManager; + @Mock + private NetworkOrchestrationService networkManager; + + @InjectMocks + private DiagnosticsServiceImpl diagnosticsService = new DiagnosticsServiceImpl(); + + @Before + public void setUp() throws Exception { + Mockito.when(diagnosticsCmd.getId()).thenReturn(1L); + Mockito.when(diagnosticsCmd.getType()).thenReturn(DiagnosticsType.PING); + Mockito.when(instanceDao.findByIdTypes(Mockito.anyLong(), Mockito.any(VirtualMachine.Type.class), + Mockito.any(VirtualMachine.Type.class), Mockito.any(VirtualMachine.Type.class))).thenReturn(instanceVO); + + } + + @After + public void tearDown() throws Exception { + Mockito.reset(diagnosticsCmd); + Mockito.reset(agentManager); + Mockito.reset(instanceDao); + Mockito.reset(instanceVO); + Mockito.reset(command); + } + + @Test + public void testRunDiagnosticsCommandTrue() throws Exception { + Mockito.when(diagnosticsCmd.getAddress()).thenReturn("8.8.8.8"); + Map accessDetailsMap = new HashMap<>(); + accessDetailsMap.put(NetworkElementCommand.ROUTER_IP, "169.20.175.10"); + Mockito.when(networkManager.getSystemVMAccessDetails(Mockito.any(VMInstanceVO.class))).thenReturn(accessDetailsMap); + final String details = "PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n" + + "64 bytes from 8.8.8.8: icmp_seq=1 ttl=125 time=7.88 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=2 ttl=125 time=251 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=3 ttl=125 time=64.9 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=4 ttl=125 time=50.7 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=5 ttl=125 time=67.9 ms\n" + + "\n" + + "--- 8.8.8.8 ping statistics ---\n" + + "5 packets transmitted, 5 received, 0% packet loss, time 4003ms\n" + + "rtt min/avg/max/mdev = 7.881/88.587/251.410/84.191 ms&&\n" + + "&&\n" + + "0\n"; + + Mockito.when(agentManager.easySend(Mockito.anyLong(), Mockito.any(DiagnosticsCommand.class))).thenReturn(new DiagnosticsAnswer(command, true, details)); + + Map detailsMap = diagnosticsService.runDiagnosticsCommand(diagnosticsCmd); + + String stdout = "PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n" + + "64 bytes from 8.8.8.8: icmp_seq=1 ttl=125 time=7.88 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=2 ttl=125 time=251 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=3 ttl=125 time=64.9 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=4 ttl=125 time=50.7 ms\n" + + "64 bytes from 8.8.8.8: icmp_seq=5 ttl=125 time=67.9 ms\n" + + "\n" + + "--- 8.8.8.8 ping statistics ---\n" + + "5 packets transmitted, 5 received, 0% packet loss, time 4003ms\n" + + "rtt min/avg/max/mdev = 7.881/88.587/251.410/84.191 ms"; + + assertEquals(3, detailsMap.size()); + assertEquals("Mismatch between actual and expected STDERR", "", detailsMap.get(ApiConstants.STDERR)); + assertEquals("Mismatch between actual and expected EXITCODE", "0", detailsMap.get(ApiConstants.EXITCODE)); + assertEquals("Mismatch between actual and expected STDOUT", stdout, detailsMap.get(ApiConstants.STDOUT)); + } + + @Test + public void testRunDiagnosticsCommandFalse() throws Exception { + Mockito.when(diagnosticsCmd.getAddress()).thenReturn("192.0.2.2"); + + Map accessDetailsMap = new HashMap<>(); + accessDetailsMap.put(NetworkElementCommand.ROUTER_IP, "169.20.175.10"); + Mockito.when(networkManager.getSystemVMAccessDetails(Mockito.any(VMInstanceVO.class))).thenReturn(accessDetailsMap); + + String details = "PING 192.0.2.2 (192.0.2.2): 56 data bytes\n" + + "76 bytes from 213.130.48.253: Destination Net Unreachable\n" + + "--- 192.0.2.2 ping statistics ---\n" + + "4 packets transmitted, 0 packets received, 100% packet loss&&\n" + + "&&\n" + + "1\n"; + String stdout = "PING 192.0.2.2 (192.0.2.2): 56 data bytes\n" + + "76 bytes from 213.130.48.253: Destination Net Unreachable\n" + + "--- 192.0.2.2 ping statistics ---\n" + + "4 packets transmitted, 0 packets received, 100% packet loss"; + Mockito.when(agentManager.easySend(Mockito.anyLong(), Mockito.any(DiagnosticsCommand.class))).thenReturn(new DiagnosticsAnswer(command, true, details)); + + Map detailsMap = diagnosticsService.runDiagnosticsCommand(diagnosticsCmd); + + assertEquals(3, detailsMap.size()); + assertEquals("Mismatch between actual and expected STDERR", "", detailsMap.get(ApiConstants.STDERR)); + assertTrue("Mismatch between actual and expected EXITCODE", !detailsMap.get(ApiConstants.EXITCODE).equalsIgnoreCase("0")); + assertEquals("Mismatch between actual and expected STDOUT", stdout, detailsMap.get(ApiConstants.STDOUT)); + } + + @Test(expected = InvalidParameterValueException.class) + public void testRunDiagnosticsThrowsInvalidParamException() throws Exception { + Mockito.when(diagnosticsCmd.getAddress()).thenReturn(""); + Mockito.when(instanceDao.findByIdTypes(Mockito.anyLong(), Mockito.any(VirtualMachine.Type.class), + Mockito.any(VirtualMachine.Type.class), Mockito.any(VirtualMachine.Type.class))).thenReturn(null); + + diagnosticsService.runDiagnosticsCommand(diagnosticsCmd); + } + + @Test(expected = CloudRuntimeException.class) + public void testVMControlIPisNull() throws Exception { + Mockito.when(diagnosticsCmd.getAddress()).thenReturn("0.42.42.42"); + + Map accessDetailsMap = new HashMap<>(); + accessDetailsMap.put(NetworkElementCommand.ROUTER_IP, null); + Mockito.when(networkManager.getSystemVMAccessDetails(Mockito.any(VMInstanceVO.class))).thenReturn(accessDetailsMap); + + diagnosticsService.runDiagnosticsCommand(diagnosticsCmd); + } + + @Test + public void testInvalidCharsInParams() throws Exception { + assertFalse(diagnosticsService.hasValidChars("'\\''")); + assertFalse(diagnosticsService.hasValidChars("-I eth0 &")); + assertFalse(diagnosticsService.hasValidChars("-I eth0 ;")); + assertFalse(diagnosticsService.hasValidChars(" &2 > ")); + assertFalse(diagnosticsService.hasValidChars(" &2 >> ")); + assertFalse(diagnosticsService.hasValidChars(" | ")); + assertFalse(diagnosticsService.hasValidChars("|")); + assertFalse(diagnosticsService.hasValidChars(",")); + } + + @Test + public void testValidCharsInParams() throws Exception { + assertTrue(diagnosticsService.hasValidChars("")); + assertTrue(diagnosticsService.hasValidChars(".")); + assertTrue(diagnosticsService.hasValidChars(" ")); + assertTrue(diagnosticsService.hasValidChars("-I eth0 www.google.com")); + assertTrue(diagnosticsService.hasValidChars(" ")); + assertTrue(diagnosticsService.hasValidChars(" -I cloudbr0 --sport ")); + assertTrue(diagnosticsService.hasValidChars(" --back -m20 ")); + assertTrue(diagnosticsService.hasValidChars("-c 5 -4")); + assertTrue(diagnosticsService.hasValidChars("-c 5 -4 -AbDfhqUV")); + } +} \ No newline at end of file diff --git a/systemvm/debian/opt/cloud/bin/diagnostics.py b/systemvm/debian/opt/cloud/bin/diagnostics.py new file mode 100755 index 000000000000..477f99d9d3fb --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/diagnostics.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# 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. + +import shlex +import subprocess +import sys + + +def run_cmd(command): + if command is not None: + try: + p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + return_code = p.returncode + + except OSError as e: + stdout = "Check your command type" + stderr = "Exception occurred: %s" % e + return_code = 1 + + finally: + print('%s&&' % stdout.strip()) + print('%s&&' % stderr.strip()) + print('%s' % return_code) + + +def get_command(): + arguments = sys.argv + cmd = " ".join(arguments[1:]) + cmd_type = sys.argv[1] + + if cmd_type == 'ping': + if '-c' in arguments: + return cmd + else: + return cmd + " -c 4" + + elif cmd_type == 'traceroute': + if '-m' in arguments: + return cmd + else: + return cmd + " -m 20" + + elif cmd_type == 'arping': + if '-c' in arguments: + return cmd + else: + return cmd + " -c 4" + + else: + return None + + +if __name__ == "__main__": + command = get_command() + run_cmd(command) diff --git a/test/integration/plugins/solidfire/TestAddRemoveHosts.py b/test/integration/plugins/solidfire/TestAddRemoveHosts.py index d9118dd6cab4..1dd29bbcf1c0 100644 --- a/test/integration/plugins/solidfire/TestAddRemoveHosts.py +++ b/test/integration/plugins/solidfire/TestAddRemoveHosts.py @@ -34,7 +34,7 @@ from marvin.lib.base import Account, ServiceOffering, User, Host, StoragePool, VirtualMachine # common - commonly used methods for all tests are listed here -from marvin.lib.common import get_domain, get_template, get_zone, list_hosts, list_clusters, list_volumes +from marvin.lib.common import get_domain, get_template, get_zone, list_clusters, list_hosts, list_volumes # utils - utility classes for common cleanup, external library wrappers, etc. from marvin.lib.utils import cleanup_resources @@ -46,14 +46,15 @@ # # Running the tests: # Change the "hypervisor_type" variable to control which hypervisor type to test. -# If using XenServer, set a breakpoint on each test after the first one. When the breakpoint is hit, reset the added/removed -# host to a snapshot state and re-start it. Once it's up and running, run the test code. +# If using XenServer, set a breakpoint on each test after the first one. When the breakpoint is hit, reset the +# added/removed host to a snapshot state and re-start it. Once it's up and running, run the test code. # Check that ip_address_of_new_xenserver_host / ip_address_of_new_kvm_host is correct. # If using XenServer, verify the "xen_server_master_hostname" variable is correct. # If using KVM, verify the "kvm_1_ip_address" variable is correct. # # Note: -# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] +# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] and +# this variable's value: TestData.clusterId. class TestData: @@ -95,18 +96,18 @@ class TestData: # modify to control which hypervisor type to test hypervisor_type = xenServer xen_server_master_hostname = "XenServer-6.5-1" - kvm_1_ip_address = "10.117.40.112" - ip_address_of_new_xenserver_host = "10.117.40.107" - ip_address_of_new_kvm_host = "10.117.40.116" + kvm_1_ip_address = "10.117.40.111" + ip_address_of_new_xenserver_host = "10.117.40.118" + ip_address_of_new_kvm_host = "10.117.40.115" def __init__(self): self.testdata = { TestData.solidFire: { - TestData.mvip: "10.117.40.120", + TestData.mvip: "10.117.78.225", TestData.username: "admin", TestData.password: "admin", TestData.port: 443, - TestData.url: "https://10.117.40.120:443" + TestData.url: "https://10.117.78.225:443" }, TestData.kvm: { TestData.username: "root", @@ -147,7 +148,7 @@ def __init__(self): TestData.primaryStorage: { TestData.name: "SolidFire-%d" % random.randint(0, 100), TestData.scope: "ZONE", - TestData.url: "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + TestData.url: "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "clusterDefaultMinIops=10000;clusterDefaultMaxIops=15000;" + "clusterDefaultBurstIopsPercentOfMaxIops=1.5;", @@ -160,7 +161,7 @@ def __init__(self): TestData.primaryStorage2: { TestData.name: "SolidFireShared-%d" % random.randint(0, 100), TestData.scope: "CLUSTER", - TestData.url: "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + TestData.url: "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "minIops=5000;maxIops=50000;burstIops=75000", TestData.provider: "SolidFireShared", @@ -454,6 +455,143 @@ def test_add_remove_host_with_solidfire_plugin_4(self): self._perform_add_remove_xenserver_host(primary_storage_2.id, sf_iscsi_name) + # Make sure each host is in its own VAG. + # Create a VM that needs a new volume from the storage that has a VAG per host. + # Verify the volume is in all VAGs. + # Remove one of the hosts. + # Check that the IQN is no longer in its previous VAG, but that the volume ID is still in that VAG, though. + # Add the host back into the cluster. The IQN should be added to a VAG that already has an IQN from this cluster in it. + def test_vag_per_host_5(self): + hosts = list_hosts(self.apiClient, clusterid=self.cluster.id) + + self.assertTrue( + len(hosts) >= 2, + "There needs to be at least two hosts." + ) + + unique_vag_ids = self._get_unique_vag_ids(hosts) + + self.assertTrue(len(hosts) == len(unique_vag_ids), "To run this test, each host should be in its own VAG.") + + primarystorage = self.testdata[TestData.primaryStorage] + + primary_storage = StoragePool.create( + self.apiClient, + primarystorage, + scope=primarystorage[TestData.scope], + zoneid=self.zone.id, + provider=primarystorage[TestData.provider], + tags=primarystorage[TestData.tags], + capacityiops=primarystorage[TestData.capacityIops], + capacitybytes=primarystorage[TestData.capacityBytes], + hypervisor=primarystorage[TestData.hypervisor] + ) + + self.cleanup.append(primary_storage) + + self.virtual_machine = VirtualMachine.create( + self.apiClient, + self.testdata[TestData.virtualMachine], + accountid=self.account.name, + zoneid=self.zone.id, + serviceofferingid=self.compute_offering.id, + templateid=self.template.id, + domainid=self.domain.id, + startvm=True + ) + + root_volume = self._get_root_volume(self.virtual_machine) + + sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, primary_storage.id, self, TestAddRemoveHosts._sf_account_id_should_be_non_zero_int_err_msg) + + sf_volumes = sf_util.get_active_sf_volumes(self.sfe, sf_account_id) + + sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, root_volume.name, self) + + sf_vag_ids = sf_util.get_vag_ids(self.cs_api, self.cluster.id, primary_storage.id, self) + + sf_util.check_vags(sf_volume, sf_vag_ids, self) + + host = Host(hosts[0].__dict__) + + host_iqn = self._get_host_iqn(host) + + all_vags = sf_util.get_all_vags(self.sfe) + + host_vag = self._get_host_vag(host_iqn, all_vags) + + self.assertTrue(host_vag != None, "The host should be in a VAG.") + + host.delete(self.apiClient) + + sf_volumes = sf_util.get_active_sf_volumes(self.sfe, sf_account_id) + + sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, root_volume.name, self) + + sf_util.check_vags(sf_volume, sf_vag_ids, self) + + all_vags = sf_util.get_all_vags(self.sfe) + + host_vag = self._get_host_vag(host_iqn, all_vags) + + self.assertTrue(host_vag == None, "The host should not be in a VAG.") + + details = { + TestData.username: "root", + TestData.password: "solidfire", + TestData.url: "http://" + host.ipaddress, + TestData.podId : host.podid, + TestData.zoneId: host.zoneid + } + + host = Host.create( + self.apiClient, + self.cluster, + details, + hypervisor=host.hypervisor + ) + + self.assertTrue( + isinstance(host, Host), + "'host' is not a 'Host'." + ) + + hosts = list_hosts(self.apiClient, clusterid=self.cluster.id) + + unique_vag_ids = self._get_unique_vag_ids(hosts) + + self.assertTrue(len(hosts) == len(unique_vag_ids) + 1, "There should be one more host than unique VAG.") + + def _get_unique_vag_ids(self, hosts): + all_vags = sf_util.get_all_vags(self.sfe) + + unique_vag_ids = [] + + for host in hosts: + host = Host(host.__dict__) + + host_iqn = self._get_host_iqn(host) + + host_vag = self._get_host_vag(host_iqn, all_vags) + + if host_vag != None and host_vag.volume_access_group_id not in unique_vag_ids: + unique_vag_ids.append(host_vag.volume_access_group_id) + + return unique_vag_ids + + def _get_host_vag(self, host_iqn, vags): + self.assertTrue(host_iqn, "'host_iqn' should not be 'None'.") + self.assertTrue(vags, "'vags' should not be 'None'.") + + self.assertTrue(isinstance(host_iqn, basestring), "'host_iqn' should be a 'string'.") + self.assertTrue(isinstance(vags, list), "'vags' should be a 'list'.") + + for vag in vags: + if host_iqn in vag.initiators: + return vag + + return None + def _perform_add_remove_xenserver_host(self, primary_storage_id, sr_name): xen_sr = self.xen_session.xenapi.SR.get_by_name_label(sr_name)[0] @@ -463,7 +601,7 @@ def _perform_add_remove_xenserver_host(self, primary_storage_id, sr_name): num_pbds = len(pbds) - sf_vag_id = self._get_sf_vag_id(self.cluster.id, primary_storage_id) + sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, primary_storage_id, self) host_iscsi_iqns = self._get_xenserver_host_iscsi_iqns() @@ -604,7 +742,7 @@ def _perform_add_remove_xenserver_host(self, primary_storage_id, sr_name): ) def _perform_add_remove_kvm_host(self, primary_storage_id): - sf_vag_id = self._get_sf_vag_id(self.cluster.id, primary_storage_id) + sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, primary_storage_id, self) kvm_login = self.testdata[TestData.kvm] @@ -720,6 +858,14 @@ def _get_iqn_2(self, primary_storage): return sql_result[0][0] + def _get_host_iqn(self, host): + sql_query = "Select url From host Where uuid = '" + str(host.id) + "'" + + # make sure you can connect to MySQL: https://teamtreehouse.com/community/cant-connect-remotely-to-mysql-server-with-mysql-workbench + sql_result = self.dbConnection.execute(sql_query) + + return sql_result[0][0] + def _get_xenserver_host_iscsi_iqns(self): hosts = self.xen_session.xenapi.host.get_all() @@ -767,20 +913,6 @@ def _get_kvm_iqn(self, ip_address, username, password): return result[len(searchFor):].strip() - def _get_sf_vag_id(self, cluster_id, primary_storage_id): - # Get SF Volume Access Group ID - sf_vag_id_request = {'clusterid': cluster_id, 'storageid': primary_storage_id} - sf_vag_id_result = self.cs_api.getSolidFireVolumeAccessGroupId(sf_vag_id_request) - sf_vag_id = sf_vag_id_result['apisolidfirevolumeaccessgroupid']['solidFireVolumeAccessGroupId'] - - self.assertEqual( - isinstance(sf_vag_id, int), - True, - TestAddRemoveHosts._vag_id_should_be_non_zero_int_err_msg - ) - - return sf_vag_id - def _get_sf_vag(self, sf_vag_id): return self.sfe.list_volume_access_groups(sf_vag_id, 1).volume_access_groups[0] diff --git a/test/integration/plugins/solidfire/TestCapacityManagement.py b/test/integration/plugins/solidfire/TestCapacityManagement.py index ab6eff124cd9..0cc9db2dae1a 100644 --- a/test/integration/plugins/solidfire/TestCapacityManagement.py +++ b/test/integration/plugins/solidfire/TestCapacityManagement.py @@ -33,7 +33,7 @@ from marvin.lib.base import Account, ServiceOffering, StoragePool, User, VirtualMachine # common - commonly used methods for all tests are listed here -from marvin.lib.common import get_domain, get_template, get_zone, list_clusters, list_hosts +from marvin.lib.common import get_domain, get_template, get_zone, list_hosts # utils - utility classes for common cleanup, external library wrappers, etc. from marvin.lib.utils import cleanup_resources @@ -47,7 +47,7 @@ # If using XenServer, verify the "xen_server_hostname" variable is correct. # # Note: -# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] +# If you do have more than one cluster, you might need to change this variable: TestData.clusterId. class TestData(): @@ -193,7 +193,6 @@ def setUpClass(cls): # Get Resources from Cloud Infrastructure cls.zone = get_zone(cls.apiClient, zone_id=cls.testdata[TestData.zoneId]) - cls.cluster = list_clusters(cls.apiClient)[0] cls.template = get_template(cls.apiClient, cls.zone.id, hypervisor=TestData.hypervisor_type) cls.domain = get_domain(cls.apiClient, cls.testdata[TestData.domainId]) diff --git a/test/integration/plugins/solidfire/TestSnapshots.py b/test/integration/plugins/solidfire/TestSnapshots.py index fab509e4b699..d9ab3b2b3e98 100644 --- a/test/integration/plugins/solidfire/TestSnapshots.py +++ b/test/integration/plugins/solidfire/TestSnapshots.py @@ -35,7 +35,7 @@ from marvin.lib.base import Account, DiskOffering, ServiceOffering, Snapshot, StoragePool, Template, User, VirtualMachine, Volume # common - commonly used methods for all tests are listed here -from marvin.lib.common import get_domain, get_template, get_zone, list_clusters, list_volumes, list_snapshots +from marvin.lib.common import get_domain, get_template, get_zone, list_volumes, list_snapshots # utils - utility classes for common cleanup, external library wrappers, etc. from marvin.lib.utils import cleanup_resources, wait_until @@ -87,16 +87,16 @@ class TestData(): def __init__(self): self.testdata = { TestData.solidFire: { - TestData.mvip: "10.117.40.120", + TestData.mvip: "10.117.78.225", TestData.username: "admin", TestData.password: "admin", TestData.port: 443, - TestData.url: "https://10.117.40.120:443" + TestData.url: "https://10.117.78.225:443" }, TestData.primaryStorage: { "name": "SolidFire-%d" % random.randint(0, 100), TestData.scope: "ZONE", - "url": "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + "url": "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "clusterDefaultMinIops=10000;clusterDefaultMaxIops=15000;" + "clusterDefaultBurstIopsPercentOfMaxIops=1.5;", @@ -155,7 +155,6 @@ def __init__(self): TestData.diskName: "test-volume-2", }, TestData.zoneId: 1, - TestData.clusterId: 1, TestData.domainId: 1, TestData.url: "10.117.40.114" } @@ -198,7 +197,6 @@ def setUpClass(cls): # Get Resources from Cloud Infrastructure cls.zone = get_zone(cls.apiClient, zone_id=cls.testdata[TestData.zoneId]) - cls.cluster = list_clusters(cls.apiClient)[0] cls.template = get_template(cls.apiClient, cls.zone.id, hypervisor=TestData.hypervisor_type) cls.domain = get_domain(cls.apiClient, cls.testdata[TestData.domainId]) diff --git a/test/integration/plugins/solidfire/TestUploadDownload.py b/test/integration/plugins/solidfire/TestUploadDownload.py index d81600e9128f..a15f27b98bf8 100644 --- a/test/integration/plugins/solidfire/TestUploadDownload.py +++ b/test/integration/plugins/solidfire/TestUploadDownload.py @@ -185,7 +185,7 @@ def setUpClass(cls): # Get Resources from Cloud Infrastructure cls.zone = get_zone(cls.apiClient, zone_id=cls.testdata[TestData.zoneId]) - cls.cluster = list_clusters(cls.apiClient)[1] + cls.cluster = list_clusters(cls.apiClient)[0] cls.template = get_template(cls.apiClient, cls.zone.id, hypervisor=TestData.hypervisor_type) cls.domain = get_domain(cls.apiClient, cls.testdata[TestData.domainId]) diff --git a/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py b/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py index 93ab3b6ff61d..7da6c9d542b8 100644 --- a/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py +++ b/test/integration/plugins/solidfire/TestVMMigrationWithStorage.py @@ -40,6 +40,9 @@ # Only one zone # Only one pod # Two clusters (have system VMs (including the VR) running on local or NFS storage) +# +# Running the tests: +# Verify the "xen_server_hostname_src" and "xen_server_hostname_dest" variables are correct. class TestData(): @@ -81,6 +84,9 @@ class TestData(): xenServer = "xenserver" zoneId = "zoneid" + xen_server_hostname_src = "XenServer-6.5-1" + xen_server_hostname_dest = "XenServer-6.5-3" + def __init__(self): self.testdata = { TestData.solidFire: { @@ -233,7 +239,7 @@ def setUpClass(cls): # Set up xenAPI connection host_ip = "https://" + \ - list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId1], name="XenServer-6.5-1")[0].ipaddress + list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId1], name=TestData.xen_server_hostname_src)[0].ipaddress # Set up XenAPI connection cls.xen_session_1 = XenAPI.Session(host_ip) @@ -242,7 +248,7 @@ def setUpClass(cls): # Set up xenAPI connection host_ip = "https://" + \ - list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId2], name="XenServer-6.5-3")[0].ipaddress + list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId2], name=TestData.xen_server_hostname_dest)[0].ipaddress # Set up XenAPI connection cls.xen_session_2 = XenAPI.Session(host_ip) @@ -532,9 +538,9 @@ def _get_source_and_dest_hosts(self): hosts = list_hosts(self.apiClient) for host in hosts: - if host.name == "XenServer-6.5-1": + if host.name == TestData.xen_server_hostname_src: src_host = host - elif host.name == "XenServer-6.5-3": + elif host.name == TestData.xen_server_hostname_dest: dest_host = host self.assertIsNotNone(src_host, "Could not locate the source host") diff --git a/test/integration/plugins/solidfire/TestVMSnapshots.py b/test/integration/plugins/solidfire/TestVMSnapshots.py index 45c42429843e..106d5836fa73 100644 --- a/test/integration/plugins/solidfire/TestVMSnapshots.py +++ b/test/integration/plugins/solidfire/TestVMSnapshots.py @@ -44,6 +44,10 @@ # Only one pod # Only one cluster +# Running the tests: +# Change the "hypervisor_type" variable to control which hypervisor type to test. +# If using XenServer, verify the "xen_server_hostname" variable is correct. + class TestData: account = "account" @@ -74,6 +78,7 @@ class TestData: # modify to control which hypervisor type to test hypervisor_type = xenServer + xen_server_hostname = "XenServer-6.5-1" def __init__(self): self.testdata = { @@ -129,7 +134,7 @@ def __init__(self): "customizediops": False, "miniops": "10000", "maxiops": "15000", - "hypervisorsnapshotreserve": 200, + "hypervisorsnapshotreserve": 400, TestData.tags: TestData.storageTag }, TestData.diskOffering: { @@ -139,7 +144,7 @@ def __init__(self): "customizediops": False, "miniops": 300, "maxiops": 500, - "hypervisorsnapshotreserve": 200, + "hypervisorsnapshotreserve": 400, TestData.tags: TestData.storageTag, "storagetype": "shared" }, @@ -179,7 +184,7 @@ def setUpClass(cls): # Set up XenAPI connection host_ip = "https://" + \ - list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId], name="XenServer-6.5-1")[0].ipaddress + list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId], name=TestData.xen_server_hostname)[0].ipaddress cls.xen_session = XenAPI.Session(host_ip) diff --git a/test/integration/plugins/solidfire/TestVolumes.py b/test/integration/plugins/solidfire/TestVolumes.py index 9d3ab2f43ee6..9685e509cb8d 100644 --- a/test/integration/plugins/solidfire/TestVolumes.py +++ b/test/integration/plugins/solidfire/TestVolumes.py @@ -51,7 +51,8 @@ # If using XenServer, change the "supports_cloning" variable to True or False as desired. # # Note: -# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] +# If you do have more than one cluster, you might need to change this line: cls.cluster = list_clusters(cls.apiClient)[0] and +# this variable's value: TestData.clusterId. class TestData(): @@ -79,6 +80,7 @@ class TestData(): tags = "tags" templateCacheNameKvm = "centos55-x86-64" templateCacheNameXenServer = "centos56-x86-64-xen" + # templateCacheNameXenServer = "centos65-x86-64-XenServer" testAccount = "testaccount" url = "url" user = "user" @@ -91,17 +93,17 @@ class TestData(): zoneId = "zoneId" # modify to control which hypervisor type to test - hypervisor_type = kvm + hypervisor_type = xenServer xen_server_hostname = "XenServer-6.5-1" def __init__(self): self.testdata = { TestData.solidFire: { - TestData.mvip: "10.117.40.120", + TestData.mvip: "10.117.78.225", TestData.username: "admin", TestData.password: "admin", TestData.port: 443, - TestData.url: "https://10.117.40.120:443" + TestData.url: "https://10.117.78.225:443" }, TestData.kvm: { TestData.username: "root", @@ -135,7 +137,7 @@ def __init__(self): TestData.primaryStorage: { "name": "SolidFire-%d" % random.randint(0, 100), TestData.scope: "ZONE", - "url": "MVIP=10.117.40.120;SVIP=10.117.41.120;" + + "url": "MVIP=10.117.78.225;SVIP=10.117.94.225;" + "clusterAdminUsername=admin;clusterAdminPassword=admin;" + "clusterDefaultMinIops=10000;clusterDefaultMaxIops=15000;" + "clusterDefaultBurstIopsPercentOfMaxIops=1.5;", diff --git a/test/integration/smoke/test_diagnostics.py b/test/integration/smoke/test_diagnostics.py new file mode 100644 index 000000000000..6364d83eeee8 --- /dev/null +++ b/test/integration/smoke/test_diagnostics.py @@ -0,0 +1,539 @@ +# 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. +""" BVT tests for remote diagnostics of system VMs +""" +# Import Local Modules +from marvin.codes import FAILED +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import runDiagnostics +from marvin.lib.utils import (cleanup_resources) +from marvin.lib.base import (Account, + ServiceOffering, + VirtualMachine) +from marvin.lib.common import (get_domain, + get_zone, + get_test_template, + list_ssvms, + list_routers) + +from nose.plugins.attrib import attr + + +class TestRemoteDiagnostics(cloudstackTestCase): + """ + Test remote diagnostics with system VMs and VR as root admin + """ + + @classmethod + def setUpClass(cls): + + testClient = super(TestRemoteDiagnostics, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.hypervisor = testClient.getHypervisorInfo() + cls.services['mode'] = cls.zone.networktype + template = get_test_template( + cls.apiclient, + cls.zone.id, + cls.hypervisor + ) + if template == FAILED: + cls.fail("get_test_template() failed to return template") + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + # Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["tiny"] + ) + cls.vm_1 = VirtualMachine.create( + cls.apiclient, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.account, + cls.service_offering + ] + + @classmethod + def tearDownClass(cls): + try: + cls.apiclient = super( + TestRemoteDiagnostics, + cls + ).getClsTestClient().getApiClient() + # Clean up, terminate the created templates + cleanup_resources(cls.apiclient, cls.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.hypervisor = self.testClient.getHypervisorInfo() + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_01_ping_in_vr_success(self): + ''' + Test Ping command execution in VR + ''' + + # Validate the following: + # 1. Ping command is executed remotely on VR + + list_router_response = list_routers( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + self.debug('Starting the router with ID: %s' % router.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = router.id + cmd.ipaddress = '8.8.8.8' + cmd.type = 'ping' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Ping in VR') + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_02_ping_in_vr_failure(self): + ''' + Test Ping command execution in VR + ''' + + # Validate the following: + # 1. Ping command is executed remotely on VR + # 2. Validate Ping command execution with a non-existent/pingable IP address + + if self.hypervisor.lower() == 'simulator': + raise self.skipTest("Skipping negative test case for Simulator hypervisor") + + list_router_response = list_routers( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + self.debug('Starting the router with ID: %s' % router.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = router.id + cmd.ipaddress = '192.0.2.2' + cmd.type = 'ping' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertNotEqual( + '0', + cmd_response.exitcode, + 'Check diagnostics command returns a non-zero exit code') + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_03_ping_in_ssvm_success(self): + ''' + Test Ping command execution in SSVM + ''' + + # Validate the following: + # 1. Ping command is executed remotely on SSVM + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_ssvm_response[0] + + self.debug('Setting up SSVM with ID %s' % ssvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = ssvm.id + cmd.ipaddress = '8.8.8.8' + cmd.type = 'ping' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Ping in SSVM' + ) + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_04_ping_in_ssvm_failure(self): + ''' + Test Ping command execution in SSVM + ''' + + # Validate the following: + # 1. Ping command is executed remotely on SSVM + # 2. Validate Ping command execution with a non-existent/pingable IP address + + if self.hypervisor.lower() == 'simulator': + raise self.skipTest("Skipping negative test case for Simulator hypervisor") + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_ssvm_response[0] + + self.debug('Setting up SSVM with ID %s' % ssvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = ssvm.id + cmd.ipaddress = '192.0.2.2' + cmd.type = 'ping' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertNotEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Ping in SSVM' + ) + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_05_ping_in_cpvm_success(self): + ''' + Test Ping command execution in CPVM + ''' + + # Validate the following: + # 1. Ping command is executed remotely on CPVM + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + cpvm = list_ssvm_response[0] + + self.debug('Setting up CPVM with ID %s' % cpvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = cpvm.id + cmd.ipaddress = '8.8.8.8' + cmd.type = 'ping' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Ping in CPVM' + ) + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_06_ping_in_cpvm_failure(self): + ''' + Test Ping command execution in CPVM + ''' + + # Validate the following: + # 1. Ping command is executed remotely on CPVM + # 2. Validate Ping command execution with a non-existent/pingable IP address + + if self.hypervisor.lower() == 'simulator': + raise self.skipTest("Skipping negative test case for Simulator hypervisor") + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + cpvm = list_ssvm_response[0] + + self.debug('Setting up CPVM with ID %s' % cpvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = cpvm.id + cmd.ipaddress = '192.0.2.2' + cmd.type = 'ping' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertNotEqual( + '0', + cmd_response.exitcode, + 'Check diagnostics command returns a non-zero exit code' + ) + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_07_arping_in_vr(self): + ''' + Test Arping command execution in VR + ''' + + # Validate the following: + # 1. Arping command is executed remotely on VR + + list_router_response = list_routers( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + self.debug('Starting the router with ID: %s' % router.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = router.id + cmd.ipaddress = router.gateway + cmd.type = 'arping' + cmd.params = "-I eth2" + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Arping in VR') + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_08_arping_in_ssvm(self): + ''' + Test Arping command execution in SSVM + ''' + + # Validate the following: + # 1. Arping command is executed remotely on SSVM + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_ssvm_response[0] + + self.debug('Setting up SSVM with ID %s' % ssvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = ssvm.id + cmd.ipaddress = ssvm.gateway + cmd.type = 'arping' + cmd.params = '-I eth2' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Arping in SSVM' + ) + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_09_arping_in_cpvm(self): + ''' + Test Arping command execution in CPVM + ''' + + # Validate the following: + # 1. Arping command is executed remotely on CPVM + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + ) + + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + 'Check list response returns a valid list' + ) + cpvm = list_cpvm_response[0] + + self.debug('Setting up CPVM with ID %s' % cpvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = cpvm.id + cmd.ipaddress = cpvm.gateway + cmd.type = 'arping' + cmd.params = '-I eth2' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Arping in CPVM' + ) + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_10_traceroute_in_vr(self): + ''' + Test Arping command execution in VR + ''' + + # Validate the following: + # 1. Arping command is executed remotely on VR + + list_router_response = list_routers( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + self.debug('Starting the router with ID: %s' % router.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = router.id + cmd.ipaddress = '8.8.4.4' + cmd.type = 'traceroute' + cmd.params = "-m 10" + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Arping in VR') + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_11_traceroute_in_ssvm(self): + ''' + Test Traceroute command execution in SSVM + ''' + + # Validate the following: + # 1. Traceroute command is executed remotely on SSVM + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_ssvm_response[0] + + self.debug('Setting up SSVM with ID %s' % ssvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = ssvm.id + cmd.ipaddress = '8.8.4.4' + cmd.type = 'traceroute' + cmd.params = '-m 10' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Traceroute in SSVM' + ) + + @attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true") + def test_12_traceroute_in_cpvm(self): + ''' + Test Traceroute command execution in CPVMM + ''' + + # Validate the following: + # 1. Traceroute command is executed remotely on CPVM + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + state='Running', + ) + + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + 'Check list response returns a valid list' + ) + cpvm = list_cpvm_response[0] + + self.debug('Setting up CPVMM with ID %s' % cpvm.id) + + cmd = runDiagnostics.runDiagnosticsCmd() + cmd.targetid = cpvm.id + cmd.ipaddress = '8.8.4.4' + cmd.type = 'traceroute' + cmd.params = '-m 10' + cmd_response = self.apiclient.runDiagnostics(cmd) + + self.assertEqual( + '0', + cmd_response.exitcode, + 'Failed to run remote Traceroute in CPVM' + ) diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index a025efefcbcb..f45f030ac403 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -190,7 +190,8 @@ 'CA': 'Certificate', 'listElastistorInterface': 'Misc', 'cloudian': 'Cloudian', - 'Sioc' : 'Sioc' + 'Sioc' : 'Sioc', + 'Diagnostics': 'Diagnostics' } diff --git a/ui/lib/jquery-ui/css/images/ui-bg_flat_0_aaaaaa_40x100.png b/ui/lib/jquery-ui/css/images/ui-bg_flat_0_aaaaaa_40x100.png deleted file mode 100755 index 5b5dab2ab7b1..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_flat_0_aaaaaa_40x100.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-bg_flat_75_ffffff_40x100.png b/ui/lib/jquery-ui/css/images/ui-bg_flat_75_ffffff_40x100.png deleted file mode 100755 index ac8b229af950..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_flat_75_ffffff_40x100.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/ui/lib/jquery-ui/css/images/ui-bg_glass_55_fbf9ee_1x400.png deleted file mode 100755 index ad3d6346e00f..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_glass_55_fbf9ee_1x400.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-bg_glass_65_ffffff_1x400.png b/ui/lib/jquery-ui/css/images/ui-bg_glass_65_ffffff_1x400.png deleted file mode 100755 index 42ccba269b6e..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_glass_65_ffffff_1x400.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-bg_glass_75_dadada_1x400.png b/ui/lib/jquery-ui/css/images/ui-bg_glass_75_dadada_1x400.png deleted file mode 100755 index 5a46b47cb166..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_glass_75_dadada_1x400.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/ui/lib/jquery-ui/css/images/ui-bg_glass_75_e6e6e6_1x400.png deleted file mode 100755 index 86c2baa655ea..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_glass_75_e6e6e6_1x400.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-bg_highlight-hard_75_a7c1d2_1x100.png b/ui/lib/jquery-ui/css/images/ui-bg_highlight-hard_75_a7c1d2_1x100.png deleted file mode 100755 index 7f5be8acd1c9..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_highlight-hard_75_a7c1d2_1x100.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png b/ui/lib/jquery-ui/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png deleted file mode 100755 index 0e05810fffe0..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-icons_222222_256x240.png b/ui/lib/jquery-ui/css/images/ui-icons_222222_256x240.png deleted file mode 100755 index b273ff111d21..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-icons_222222_256x240.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-icons_2e83ff_256x240.png b/ui/lib/jquery-ui/css/images/ui-icons_2e83ff_256x240.png deleted file mode 100755 index 09d1cdc856c2..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-icons_2e83ff_256x240.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-icons_454545_256x240.png b/ui/lib/jquery-ui/css/images/ui-icons_454545_256x240.png deleted file mode 100755 index 59bd45b907c4..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-icons_454545_256x240.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-icons_888888_256x240.png b/ui/lib/jquery-ui/css/images/ui-icons_888888_256x240.png deleted file mode 100755 index 6d02426c114b..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-icons_888888_256x240.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/images/ui-icons_cd0a0a_256x240.png b/ui/lib/jquery-ui/css/images/ui-icons_cd0a0a_256x240.png deleted file mode 100755 index 2ab019b73ec1..000000000000 Binary files a/ui/lib/jquery-ui/css/images/ui-icons_cd0a0a_256x240.png and /dev/null differ diff --git a/ui/lib/jquery-ui/css/jquery-ui.css b/ui/lib/jquery-ui/css/jquery-ui.css deleted file mode 100755 index b87bd69f6ade..000000000000 --- a/ui/lib/jquery-ui/css/jquery-ui.css +++ /dev/null @@ -1,568 +0,0 @@ -/* - * jQuery UI CSS Framework 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - */ - -/* Layout helpers -----------------------------------*/ -.ui-helper-hidden { display: none; } -.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } -.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } -.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } -.ui-helper-clearfix { display: inline-block; } -/* required comment for clearfix to work in Opera \*/ -* html .ui-helper-clearfix { height:1%; } -.ui-helper-clearfix { display:block; } -/* end clearfix */ -.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } - - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { cursor: default !important; } - - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } - - -/* - * jQuery UI CSS Framework 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=2px&bgColorHeader=a7c1d2&bgTextureHeader=04_highlight_hard.png&bgImgOpacityHeader=75&borderColorHeader=787878&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=05_inset_soft.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px - */ - - -/* Component containers -----------------------------------*/ -.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } -.ui-widget .ui-widget { font-size: 1em; } -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } -.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } -.ui-widget-content a { color: #222222; } -.ui-widget-header { border: 1px solid #787878; background: #a7c1d2 url(images/ui-bg_highlight-hard_75_a7c1d2_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; } -.ui-widget-header a { color: #222222; } - -/* Interaction states -----------------------------------*/ -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; } -.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; } -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } -.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; } -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } -.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; } -.ui-widget :active { outline: none; } - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; } -.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } -.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_inset-soft_95_fef1ec_1x100.png) 50% bottom repeat-x; color: #cd0a0a; } -.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } -.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } -.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } -.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } -.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } -.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } -.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } -.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } -.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } -.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } - -/* positioning */ -.ui-icon-carat-1-n { background-position: 0 0; } -.ui-icon-carat-1-ne { background-position: -16px 0; } -.ui-icon-carat-1-e { background-position: -32px 0; } -.ui-icon-carat-1-se { background-position: -48px 0; } -.ui-icon-carat-1-s { background-position: -64px 0; } -.ui-icon-carat-1-sw { background-position: -80px 0; } -.ui-icon-carat-1-w { background-position: -96px 0; } -.ui-icon-carat-1-nw { background-position: -112px 0; } -.ui-icon-carat-2-n-s { background-position: -128px 0; } -.ui-icon-carat-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -64px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -64px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 0 -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-off { background-position: -96px -144px; } -.ui-icon-radio-on { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 2px; -webkit-border-top-left-radius: 2px; -khtml-border-top-left-radius: 2px; border-top-left-radius: 2px; } -.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 2px; -webkit-border-top-right-radius: 2px; -khtml-border-top-right-radius: 2px; border-top-right-radius: 2px; } -.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 2px; -webkit-border-bottom-left-radius: 2px; -khtml-border-bottom-left-radius: 2px; border-bottom-left-radius: 2px; } -.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 2px; -webkit-border-bottom-right-radius: 2px; -khtml-border-bottom-right-radius: 2px; border-bottom-right-radius: 2px; } - -/* Overlays */ -.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } -.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* - * jQuery UI Resizable 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Resizable#theming - */ -.ui-resizable { position: relative;} -.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; } -.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } -.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } -.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } -.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } -.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } -.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } -.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } -.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } -.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* - * jQuery UI Selectable 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectable#theming - */ -.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } -/* - * jQuery UI Accordion 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Accordion#theming - */ -/* IE/Win - Fix animation bug - #4615 */ -.ui-accordion { width: 100%; } -.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } -.ui-accordion .ui-accordion-li-fix { display: inline; } -.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } -.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } -.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } -.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } -.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } -.ui-accordion .ui-accordion-content-active { display: block; } -/* - * jQuery UI Autocomplete 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Autocomplete#theming - */ -.ui-autocomplete { position: absolute; cursor: default; } - -/* workarounds */ -* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ - -/* - * jQuery UI Menu 1.8.14 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Menu#theming - */ -.ui-menu { - list-style:none; - padding: 2px; - margin: 0; - display:block; - float: left; -} -.ui-menu .ui-menu { - margin-top: -3px; -} -.ui-menu .ui-menu-item { - margin:0; - padding: 0; - zoom: 1; - float: left; - clear: left; - width: 100%; -} -.ui-menu .ui-menu-item a { - text-decoration:none; - display:block; - padding:.2em .4em; - line-height:1.5; - zoom:1; -} -.ui-menu .ui-menu-item a.ui-state-hover, -.ui-menu .ui-menu-item a.ui-state-active { - font-weight: normal; - margin: -1px; -} -/* - * jQuery UI Button 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Button#theming - */ -.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ -.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ -button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ -.ui-button-icons-only { width: 3.4em; } -button.ui-button-icons-only { width: 3.7em; } - -/*button text element */ -.ui-button .ui-button-text { display: block; line-height: 1.4; } -.ui-button-text-only .ui-button-text { padding: .4em 1em; } -.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } -.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } -.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } -.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } -/* no icon support for input elements, provide padding by default */ -input.ui-button { padding: .4em 1em; } - -/*button icon element(s) */ -.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } -.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } -.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } -.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } -.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } - -/*button sets*/ -.ui-buttonset { margin-right: 7px; } -.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } - -/* workarounds */ -button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ -/* - * jQuery UI Dialog 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Dialog#theming - */ -.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } -.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } -.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } -.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } -.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } -.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } -.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } -.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } -.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } -.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } -.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } -.ui-draggable .ui-dialog-titlebar { cursor: move; } -/* - * jQuery UI Slider 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Slider#theming - */ -.ui-slider { position: relative; text-align: left; } -.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } -.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } - -.ui-slider-horizontal { height: .8em; } -.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } -.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } -.ui-slider-horizontal .ui-slider-range-min { left: 0; } -.ui-slider-horizontal .ui-slider-range-max { right: 0; } - -.ui-slider-vertical { width: .8em; height: 100px; } -.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } -.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } -.ui-slider-vertical .ui-slider-range-min { bottom: 0; } -.ui-slider-vertical .ui-slider-range-max { top: 0; }/* - * jQuery UI Tabs 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs#theming - */ -.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ -.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } -.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } -.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } -.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } -.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } -.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ -.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } -.ui-tabs .ui-tabs-hide { display: none !important; } -/* - * jQuery UI Datepicker 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker#theming - */ -.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } -.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } -.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } -.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } -.ui-datepicker .ui-datepicker-prev { left:2px; } -.ui-datepicker .ui-datepicker-next { right:2px; } -.ui-datepicker .ui-datepicker-prev-hover { left:1px; } -.ui-datepicker .ui-datepicker-next-hover { right:1px; } -.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } -.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } -.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } -.ui-datepicker select.ui-datepicker-month-year {width: 100%;} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { width: 49%;} -.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } -.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } -.ui-datepicker td { border: 0; padding: 1px; } -.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } -.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } -.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } - -/* with multiple calendars */ -.ui-datepicker.ui-datepicker-multi { width:auto; } -.ui-datepicker-multi .ui-datepicker-group { float:left; } -.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } -.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } -.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } -.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } -.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } -.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } - -/* RTL support */ -.ui-datepicker-rtl { direction: rtl; } -.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } -.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } -.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } -.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } -.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } -.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } -.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } -.ui-datepicker-rtl .ui-datepicker-group { float:right; } -.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } -.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } - -/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ -.ui-datepicker-cover { - display: none; /*sorry for IE5*/ - display/**/: block; /*sorry for IE5*/ - position: absolute; /*must have*/ - z-index: -1; /*must have*/ - filter: mask(); /*must have*/ - top: -4px; /*must have*/ - left: -4px; /*must have*/ - width: 200px; /*must have*/ - height: 200px; /*must have*/ -}/* - * jQuery UI Progressbar 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar#theming - */ -.ui-progressbar { height:2em; text-align: left; } -.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file diff --git a/ui/lib/jquery-ui/index.html b/ui/lib/jquery-ui/index.html deleted file mode 100755 index 904f4dca80f1..000000000000 --- a/ui/lib/jquery-ui/index.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - jQuery UI Example Page - - - - - - - -

Welcome to jQuery UI!

-

This page demonstrates the widgets you downloaded using the theme you selected in the download builder. We've included and linked to minified versions of jQuery, your personalized copy of jQuery UI (js/jquery-ui-1.8.14.custom.min.js), and css/custom-theme/jquery-ui-1.8.14.custom.css which imports the entire jQuery UI CSS Framework. You can choose to link a subset of the CSS Framework depending on your needs.

-

You've downloaded components and a theme that are compatible with jQuery 1.3+. Please make sure you are using jQuery 1.3+ in your production environment.

- -

YOUR COMPONENTS:

- - -

Accordion

-
-
-

First

-
Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.
-
-
-

Second

-
Phasellus mattis tincidunt nibh.
-
-
-

Third

-
Nam dui erat, auctor a, dignissim quis.
-
-
- - -

Tabs

-
- -
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
-
Phasellus mattis tincidunt nibh. Cras orci urna, blandit id, pretium vel, aliquet ornare, felis. Maecenas scelerisque sem non nisl. Fusce sed lorem in enim dictum bibendum.
-
Nam dui erat, auctor a, dignissim quis, sollicitudin eu, felis. Pellentesque nisi urna, interdum eget, sagittis et, consequat vestibulum, lacus. Mauris porttitor ullamcorper augue.
-
- - -

Dialog

-

Open Dialog

- - -

Overlay and Shadow Classes (not currently used in UI widgets)

-
-

Lorem ipsum dolor sit amet, Nulla nec tortor. Donec id elit quis purus consectetur consequat.

Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci.

Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat.

Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam.

Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante.

Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi.

- - -
-
-
-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

-
-
- -
- - - -
-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

-
- - - -

Framework Icons (content color preview)

-
    - -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
  • -
  • -
  • -
  • -
- - - -

Slider

-
- - -

Datepicker

-
- - -

Progressbar

-
- - -

Highlight / Error

-
-
-

- Hey! Sample ui-state-highlight style.

-
-
-
-
-
-

- Alert: Sample ui-state-error style.

-
-
- - - - - diff --git a/ui/lib/jquery-ui/js/jquery-ui.js b/ui/lib/jquery-ui/js/jquery-ui.js index 7ec85a5741b1..31ee9cd8116b 100755 --- a/ui/lib/jquery-ui/js/jquery-ui.js +++ b/ui/lib/jquery-ui/js/jquery-ui.js @@ -1,789 +1,16617 @@ +/*! jQuery UI - v1.11.4 - 2015-03-11 +* http://jqueryui.com +* Includes: core.js, widget.js, mouse.js, position.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, draggable.js, droppable.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, progressbar.js, resizable.js, selectable.js, selectmenu.js, slider.js, sortable.js, spinner.js, tabs.js, tooltip.js +* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ + +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + } else { + + // Browser globals + factory( jQuery ); + } +}(function( $ ) { /*! - * jQuery UI 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI - */ -(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.14", -keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus(); -b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this, -"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection", -function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth, -outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b); -return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e= -0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted= -false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); -;/* - * jQuery UI Position 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Position - */ -(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY, -left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+= -k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-= -m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left= -d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+= -a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b), -g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); -;/* - * jQuery UI Draggable 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Draggables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== -"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= -this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;this.helper= -this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}); -this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true}, -_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b= -false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration, -10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle|| -!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&& -a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent= -this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"), -10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"), -10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top, -(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!= -"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"), -10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+ -this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&& -!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.leftg[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.topg[3])?h:!(h-this.offset.click.topg[2])?e:!(e-this.offset.click.left=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= -i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), -top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= -this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", -nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== -String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection(); -this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy(); -var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a= -false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"}); -this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff= -{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis]; -if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false}, -_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f, -{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight: -Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(cb.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left= -null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+ -a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+ -c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]); -b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.14"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(), -10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top- -f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType? -e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a= -e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing, -step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement= -e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset; -var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left: -a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top- -d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition, -f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25, -display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b= -e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height= -d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery); -;/* - * jQuery UI Selectable 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"), -selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("
")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX, -c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting", -c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d= -this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable"); -this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a=== -"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&& -!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top, -left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]}; -this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!= -document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a); -return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0], -e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset(); -c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"): -this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null, -dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")}, -toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith(); -if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), -this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b= -this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f= -d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")|| -0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out", -a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h- -f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g- -this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this, -this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop", -a,this._uiHash());for(e=0;e li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"); -a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); -if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion", -function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a= -this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"); -this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons(); -b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target); -a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+ -c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options; -if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header); -if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(), -e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight|| -e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false", -"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.14", -animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/); -f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide", -paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); -;/* - * jQuery UI Autocomplete 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Autocomplete - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.position.js - */ -(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.attr("readonly"))){g= -false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!= -a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)}; -this.menu=d("
    ").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&& -a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"); -d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&& -b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source= -this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length").data("item.autocomplete",b).append(d("").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, -"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery); -(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", --1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id"); -this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b, -this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| -this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| -this.first()?":last":":first"))},hasScroll:function(){return this.element.height()").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("");e.secondary&&a.append("");if(!this.options.text){d.push(f?"ui-button-icons-only": -"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")=== -"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"); -b.Widget.prototype.destroy.call(this)}})})(jQuery); -;/* - * jQuery UI Dialog 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Dialog - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.button.js - * jquery.ui.draggable.js - * jquery.ui.mouse.js - * jquery.ui.position.js - * jquery.ui.resizable.js - */ -(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false, -position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||" ",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+ -b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g), -h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id", -e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"); -a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!== -b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+= -1;d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target=== -f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("
    ").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a, -function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close", -handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition, -originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize", -f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "): -[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f); -if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"): -e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a= -this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height- -b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.14",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "), -create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(), -height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight); -b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(a.range==="min"||a.range==="max"?" ui-slider-range-"+a.range:""))}for(var j=c.length;j"); -this.handles=c.add(d(e.join("")).appendTo(b.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){a.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(a.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle", -g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!b.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!b._keySliding){b._keySliding=true;d(this).addClass("ui-state-active");i=b._start(g,l);if(i===false)return}break}m=b.options.step;i=b.options.values&&b.options.values.length? -(h=b.values(l)):(h=b.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=b._valueMin();break;case d.ui.keyCode.END:h=b._valueMax();break;case d.ui.keyCode.PAGE_UP:h=b._trimAlignValue(i+(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=b._trimAlignValue(i-(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===b._valueMax())return;h=b._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===b._valueMin())return;h=b._trimAlignValue(i- -m);break}b._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(b._keySliding){b._keySliding=false;b._stop(g,k);b._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy(); -return this},_mouseCapture:function(b){var a=this.options,c,f,e,j,g;if(a.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:b.pageX,y:b.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(a.range===true&&this.values(1)===a.min){g+=1;e=d(this.handles[g])}if(this._start(b,g)===false)return false; -this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();a=e.offset();this._clickOffset=!d(b.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:b.pageX-a.left-e.width()/2,top:b.pageY-a.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(b,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(b){var a= -this._normValueFromMouse({x:b.pageX,y:b.pageY});this._slide(b,this._handleIndex,a);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(b){var a;if(this.orientation==="horizontal"){a= -this.elementSize.width;b=b.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{a=this.elementSize.height;b=b.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}a=b/a;if(a>1)a=1;if(a<0)a=0;if(this.orientation==="vertical")a=1-a;b=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+a*b)},_start:function(b,a){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(a); -c.values=this.values()}return this._trigger("start",b,c)},_slide:function(b,a,c){var f;if(this.options.values&&this.options.values.length){f=this.values(a?0:1);if(this.options.values.length===2&&this.options.range===true&&(a===0&&c>f||a===1&&c1){this.options.values[b]=this._trimAlignValue(a);this._refreshValue();this._change(null,b)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e=this._valueMax())return this._valueMax();var a=this.options.step>0?this.options.step:1,c=(b-this._valueMin())%a;alignValue=b-c;if(Math.abs(c)*2>=a)alignValue+=c>0?a:-a;return parseFloat(alignValue.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max}, -_refreshValue:function(){var b=this.options.range,a=this.options,c=this,f=!this._animateOff?a.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,a.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},a.animate); -if(h===1)c.range[f?"animate":"css"]({width:e-g+"%"},{queue:false,duration:a.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},a.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:a.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,a.animate);if(b==="min"&&this.orientation==="horizontal")this.range.stop(1, -1)[f?"animate":"css"]({width:e+"%"},a.animate);if(b==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:a.animate});if(b==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},a.animate);if(b==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:a.animate})}}});d.extend(d.ui.slider,{version:"1.8.14"})})(jQuery); -;/* - * jQuery UI Tabs 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&& -e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b= -d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| -(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); -this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= -this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); -if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")); -this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+ -g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal", -function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")}; -this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected= --1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier."; -d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e= -d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b, -e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]); -j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove(); -if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null, -this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this}, -load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c, -"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, -url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.14"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k'))}function N(a){return a.bind("mouseout",function(b){b= -d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");b.addClass("ui-state-hover"); -b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.14"}});var A=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){H(this._defaults, -a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1"),input:a,selectedDay:0, -selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('
    '))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]= -h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c= -this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f==""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a, -"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker", -function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.dpDiv.show()}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput); -a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left", -this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus", -this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b= -b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5", -cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a); -d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c= -d.datepicker._get(b,"beforeShow");H(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c= -{left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover"); -if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing=true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv); -J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"); -a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]|| -c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+ -i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b= -this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute", -left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&& -d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth= -b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b=this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear= -!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a); -a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a)); -d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()% -100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=B+1-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y", -TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=k+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay= -a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(), -b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n= -this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m,g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+s+"":f?"":''+s+"";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&&a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,s)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),B= -this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right": -"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='
    '+(/all|left/.test(t)&&x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,B,v)+'
    ';var z=j?'": -"";for(t=0;t<7;t++){var r=(t+h)%7;z+="=5?' class="ui-datepicker-week-end"':"")+'>'+q[r]+""}y+=z+"";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q";var R=!j?"":'";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&ro;R+='";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+""}g++;if(g>11){g=0;m++}y+="
    '+this._get(a,"weekHeader")+"
    '+ -this._get(a,"calculateWeek")(r)+""+(F&&!D?" ":L?''+r.getDate()+"":''+ -r.getDate()+"")+"
    "+(l?""+(i[0]>0&&G==i[1]-1?'
    ':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'':"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"), -l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
    ',o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&&l)?" ":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()): -g;for(a.yearshtml+='";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="
    ";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c== -"Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear"); -if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); -c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, -"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= -function(a){if(!this.length)return this;if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker, -[this[0]].concat(b));return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.14";window["DP_jQuery_"+A]=d})(jQuery); -;/* - * jQuery UI Progressbar 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); -this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* -this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.14"})})(jQuery); -;/* - * jQuery UI Effects 1.8.14 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/ - */ -jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], -16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, -a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= -a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor", -"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0, -0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211, -211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, -d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})}; -f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this, -[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.14",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}); -c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c, -a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments); -a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%", -"pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d* -((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/= -e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/= -e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h= 0 ) && focusable( element, !isTabIndexNaN ); + } +}); + +// support: jQuery <1.8 +if ( !$( "" ).outerWidth( 1 ).jquery ) { + $.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; + if ( border ) { + size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; + }); +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) +if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { + $.fn.removeData = (function( removeData ) { + return function( key ) { + if ( arguments.length ) { + return removeData.call( this, $.camelCase( key ) ); + } else { + return removeData.call( this ); + } + }; + })( $.fn.removeData ); +} + +// deprecated +$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +$.fn.extend({ + focus: (function( orig ) { + return function( delay, fn ) { + return typeof delay === "number" ? + this.each(function() { + var elem = this; + setTimeout(function() { + $( elem ).focus(); + if ( fn ) { + fn.call( elem ); + } + }, delay ); + }) : + orig.apply( this, arguments ); + }; + })( $.fn.focus ), + + disableSelection: (function() { + var eventType = "onselectstart" in document.createElement( "div" ) ? + "selectstart" : + "mousedown"; + + return function() { + return this.bind( eventType + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }; + })(), + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + }, + + zIndex: function( zIndex ) { + if ( zIndex !== undefined ) { + return this.css( "zIndex", zIndex ); + } + + if ( this.length ) { + var elem = $( this[ 0 ] ), position, value; + while ( elem.length && elem[ 0 ] !== document ) { + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + //
    + value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + } +}); + +// $.ui.plugin is deprecated. Use $.widget() extensions instead. +$.ui.plugin = { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args, allowDisconnected ) { + var i, + set = instance.plugins[ name ]; + + if ( !set ) { + return; + } + + if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } +}; + + +/*! + * jQuery UI Widget 1.11.4 + * http://jqueryui.com * - * Depends: - * jquery.effects.core.js + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/jQuery.widget/ */ -(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","bottom","left","right"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/ -3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g", + options: { + disabled: false, + + // callbacks + create: null + }, + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = widget_uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } + + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this._create(); + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, + + destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .unbind( this.eventNamespace ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); + this.widget() + .unbind( this.eventNamespace ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetFullName + "-disabled " + + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + }, + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + parts, + curOption, + i; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + .toggleClass( this.widgetFullName + "-disabled", !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } + } + + return this; + }, + + enable: function() { + return this._setOptions({ disabled: false }); + }, + disable: function() { + return this._setOptions({ disabled: true }); + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement, + instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^([\w:-]*)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $( this.bindings.not( element ).get() ); + this.focusable = $( this.focusable.not( element ).get() ); + this.hoverable = $( this.hoverable.not( element ).get() ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +var widget = $.widget; + + +/*! + * jQuery UI Mouse 1.11.4 + * http://jqueryui.com * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. * http://jquery.org/license * - * http://docs.jquery.com/UI/Effects/Clip + * http://api.jqueryui.com/mouse/ + */ + + +var mouseHandled = false; +$( document ).mouseup( function() { + mouseHandled = false; +}); + +var mouse = $.widget("ui.mouse", { + version: "1.11.4", + options: { + cancel: "input,textarea,button,select,option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .bind("mousedown." + this.widgetName, function(event) { + return that._mouseDown(event); + }) + .bind("click." + this.widgetName, function(event) { + if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { + $.removeData(event.target, that.widgetName + ".preventClickEvent"); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind("." + this.widgetName); + if ( this._mouseMoveDelegate ) { + this.document + .unbind("mousemove." + this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup." + this.widgetName, this._mouseUpDelegate); + } + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if ( mouseHandled ) { + return; + } + + this._mouseMoved = false; + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = (event.which === 1), + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + that.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { + $.removeData(event.target, this.widgetName + ".preventClickEvent"); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return that._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return that._mouseUp(event); + }; + + this.document + .bind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .bind( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // Only check for mouseups outside the document if you've moved inside the document + // at least once. This prevents the firing of mouseup in the case of IE<9, which will + // fire a mousemove event if content is placed under the cursor. See #7778 + // Support: IE <9 + if ( this._mouseMoved ) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { + return this._mouseUp(event); + + // Iframe mouseup check - mouseup occurred in another document + } else if ( !event.which ) { + return this._mouseUp( event ); + } + } + + if ( event.which || event.button ) { + this._mouseMoved = true; + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + this.document + .unbind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .unbind( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target === this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + ".preventClickEvent", true); + } + + this._mouseStop(event); + } + + mouseHandled = false; + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(/* event */) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(/* event */) {}, + _mouseDrag: function(/* event */) {}, + _mouseStop: function(/* event */) {}, + _mouseCapture: function(/* event */) { return true; } +}); + + +/*! + * jQuery UI Position 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license * - * Depends: - * jquery.effects.core.js + * http://api.jqueryui.com/position/ */ -(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position, -c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Drop 1.8.14 + +(function() { + +$.ui = $.ui || {}; + +var cachedScrollbarWidth, supportsOffsetFractions, + max = Math.max, + abs = Math.abs, + round = Math.round, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +function getOffsets( offsets, width, height ) { + return [ + parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[0]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "
    " ), + innerDiv = div.children()[0]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[0].clientWidth; + } + + div.remove(); + + return (cachedScrollbarWidth = w1 - w2); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-x" ), + overflowY = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[0].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[0].scrollHeight ); + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[0] ), + isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9; + return { + element: withinElement, + isWindow: isWindow, + isDocument: isDocument, + offset: withinElement.offset() || { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + + // support: jQuery 1.6.x + // jQuery 1.6 doesn't support .outerWidth/Height() on documents or windows + width: isWindow || isDocument ? withinElement.width() : withinElement.outerWidth(), + height: isWindow || isDocument ? withinElement.height() : withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[0].preventDefault ) { + // force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + // clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + }); + + // normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each(function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + // if the browser doesn't support fractions, then round for consistent results + if ( !supportsOffsetFractions ) { + position.left = round( position.left ); + position.top = round( position.top ); + } + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem: elem + }); + } + }); + + if ( options.using ) { + // adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + }); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // element is wider than within + if ( data.collisionWidth > outerWidth ) { + // element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; + position.left += overLeft - newOverRight; + // element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + // element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + // too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + // too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + // adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // element is taller than within + if ( data.collisionHeight > outerHeight ) { + // element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; + position.top += overTop - newOverBottom; + // element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + // element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + // too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + // too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + // adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; + if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { + position.top += myOffset + atOffset + offset; + } + } else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; + if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +// fraction support test +(function() { + var testElement, testElementParent, testElementStyle, offsetLeft, i, + body = document.getElementsByTagName( "body" )[ 0 ], + div = document.createElement( "div" ); + + //Create a "fake body" for testing based on method used in jQuery.support + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + background: "none" + }; + if ( body ) { + $.extend( testElementStyle, { + position: "absolute", + left: "-1000px", + top: "-1000px" + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || document.documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + div.style.cssText = "position: absolute; left: 10.7432222px;"; + + offsetLeft = $( div ).offset().left; + supportsOffsetFractions = offsetLeft > 10 && offsetLeft < 11; + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); +})(); + +})(); + +var position = $.ui.position; + + +/*! + * jQuery UI Accordion 1.11.4 + * http://jqueryui.com * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. * http://jquery.org/license * - * http://docs.jquery.com/UI/Effects/Drop + * http://api.jqueryui.com/accordion/ + */ + + +var accordion = $.widget( "ui.accordion", { + version: "1.11.4", + options: { + active: 0, + animate: {}, + collapsible: false, + event: "click", + header: "> li > :first-child,> :not(li):even", + heightStyle: "auto", + icons: { + activeHeader: "ui-icon-triangle-1-s", + header: "ui-icon-triangle-1-e" + }, + + // callbacks + activate: null, + beforeActivate: null + }, + + hideProps: { + borderTopWidth: "hide", + borderBottomWidth: "hide", + paddingTop: "hide", + paddingBottom: "hide", + height: "hide" + }, + + showProps: { + borderTopWidth: "show", + borderBottomWidth: "show", + paddingTop: "show", + paddingBottom: "show", + height: "show" + }, + + _create: function() { + var options = this.options; + this.prevShow = this.prevHide = $(); + this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ) + // ARIA + .attr( "role", "tablist" ); + + // don't allow collapsible: false and active: false / null + if ( !options.collapsible && (options.active === false || options.active == null) ) { + options.active = 0; + } + + this._processPanels(); + // handle negative values + if ( options.active < 0 ) { + options.active += this.headers.length; + } + this._refresh(); + }, + + _getCreateEventData: function() { + return { + header: this.active, + panel: !this.active.length ? $() : this.active.next() + }; + }, + + _createIcons: function() { + var icons = this.options.icons; + if ( icons ) { + $( "" ) + .addClass( "ui-accordion-header-icon ui-icon " + icons.header ) + .prependTo( this.headers ); + this.active.children( ".ui-accordion-header-icon" ) + .removeClass( icons.header ) + .addClass( icons.activeHeader ); + this.headers.addClass( "ui-accordion-icons" ); + } + }, + + _destroyIcons: function() { + this.headers + .removeClass( "ui-accordion-icons" ) + .children( ".ui-accordion-header-icon" ) + .remove(); + }, + + _destroy: function() { + var contents; + + // clean up main element + this.element + .removeClass( "ui-accordion ui-widget ui-helper-reset" ) + .removeAttr( "role" ); + + // clean up headers + this.headers + .removeClass( "ui-accordion-header ui-accordion-header-active ui-state-default " + + "ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) + .removeAttr( "role" ) + .removeAttr( "aria-expanded" ) + .removeAttr( "aria-selected" ) + .removeAttr( "aria-controls" ) + .removeAttr( "tabIndex" ) + .removeUniqueId(); + + this._destroyIcons(); + + // clean up content panels + contents = this.headers.next() + .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom " + + "ui-accordion-content ui-accordion-content-active ui-state-disabled" ) + .css( "display", "" ) + .removeAttr( "role" ) + .removeAttr( "aria-hidden" ) + .removeAttr( "aria-labelledby" ) + .removeUniqueId(); + + if ( this.options.heightStyle !== "content" ) { + contents.css( "height", "" ); + } + }, + + _setOption: function( key, value ) { + if ( key === "active" ) { + // _activate() will handle invalid values and update this.options + this._activate( value ); + return; + } + + if ( key === "event" ) { + if ( this.options.event ) { + this._off( this.headers, this.options.event ); + } + this._setupEvents( value ); + } + + this._super( key, value ); + + // setting collapsible: false while collapsed; open first panel + if ( key === "collapsible" && !value && this.options.active === false ) { + this._activate( 0 ); + } + + if ( key === "icons" ) { + this._destroyIcons(); + if ( value ) { + this._createIcons(); + } + } + + // #5332 - opacity doesn't cascade to positioned elements in IE + // so we need to add the disabled class to the headers and panels + if ( key === "disabled" ) { + this.element + .toggleClass( "ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + this.headers.add( this.headers.next() ) + .toggleClass( "ui-state-disabled", !!value ); + } + }, + + _keydown: function( event ) { + if ( event.altKey || event.ctrlKey ) { + return; + } + + var keyCode = $.ui.keyCode, + length = this.headers.length, + currentIndex = this.headers.index( event.target ), + toFocus = false; + + switch ( event.keyCode ) { + case keyCode.RIGHT: + case keyCode.DOWN: + toFocus = this.headers[ ( currentIndex + 1 ) % length ]; + break; + case keyCode.LEFT: + case keyCode.UP: + toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; + break; + case keyCode.SPACE: + case keyCode.ENTER: + this._eventHandler( event ); + break; + case keyCode.HOME: + toFocus = this.headers[ 0 ]; + break; + case keyCode.END: + toFocus = this.headers[ length - 1 ]; + break; + } + + if ( toFocus ) { + $( event.target ).attr( "tabIndex", -1 ); + $( toFocus ).attr( "tabIndex", 0 ); + toFocus.focus(); + event.preventDefault(); + } + }, + + _panelKeyDown: function( event ) { + if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) { + $( event.currentTarget ).prev().focus(); + } + }, + + refresh: function() { + var options = this.options; + this._processPanels(); + + // was collapsed or no panel + if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) { + options.active = false; + this.active = $(); + // active false only when collapsible is true + } else if ( options.active === false ) { + this._activate( 0 ); + // was active, but active panel is gone + } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + // all remaining panel are disabled + if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) { + options.active = false; + this.active = $(); + // activate previous panel + } else { + this._activate( Math.max( 0, options.active - 1 ) ); + } + // was active, active panel still exists + } else { + // make sure active index is correct + options.active = this.headers.index( this.active ); + } + + this._destroyIcons(); + + this._refresh(); + }, + + _processPanels: function() { + var prevHeaders = this.headers, + prevPanels = this.panels; + + this.headers = this.element.find( this.options.header ) + .addClass( "ui-accordion-header ui-state-default ui-corner-all" ); + + this.panels = this.headers.next() + .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ) + .filter( ":not(.ui-accordion-content-active)" ) + .hide(); + + // Avoid memory leaks (#10056) + if ( prevPanels ) { + this._off( prevHeaders.not( this.headers ) ); + this._off( prevPanels.not( this.panels ) ); + } + }, + + _refresh: function() { + var maxHeight, + options = this.options, + heightStyle = options.heightStyle, + parent = this.element.parent(); + + this.active = this._findActive( options.active ) + .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ) + .removeClass( "ui-corner-all" ); + this.active.next() + .addClass( "ui-accordion-content-active" ) + .show(); + + this.headers + .attr( "role", "tab" ) + .each(function() { + var header = $( this ), + headerId = header.uniqueId().attr( "id" ), + panel = header.next(), + panelId = panel.uniqueId().attr( "id" ); + header.attr( "aria-controls", panelId ); + panel.attr( "aria-labelledby", headerId ); + }) + .next() + .attr( "role", "tabpanel" ); + + this.headers + .not( this.active ) + .attr({ + "aria-selected": "false", + "aria-expanded": "false", + tabIndex: -1 + }) + .next() + .attr({ + "aria-hidden": "true" + }) + .hide(); + + // make sure at least one header is in the tab order + if ( !this.active.length ) { + this.headers.eq( 0 ).attr( "tabIndex", 0 ); + } else { + this.active.attr({ + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + }) + .next() + .attr({ + "aria-hidden": "false" + }); + } + + this._createIcons(); + + this._setupEvents( options.event ); + + if ( heightStyle === "fill" ) { + maxHeight = parent.height(); + this.element.siblings( ":visible" ).each(function() { + var elem = $( this ), + position = elem.css( "position" ); + + if ( position === "absolute" || position === "fixed" ) { + return; + } + maxHeight -= elem.outerHeight( true ); + }); + + this.headers.each(function() { + maxHeight -= $( this ).outerHeight( true ); + }); + + this.headers.next() + .each(function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + }) + .css( "overflow", "auto" ); + } else if ( heightStyle === "auto" ) { + maxHeight = 0; + this.headers.next() + .each(function() { + maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() ); + }) + .height( maxHeight ); + } + }, + + _activate: function( index ) { + var active = this._findActive( index )[ 0 ]; + + // trying to activate the already active panel + if ( active === this.active[ 0 ] ) { + return; + } + + // trying to collapse, simulate a click on the currently active header + active = active || this.active[ 0 ]; + + this._eventHandler({ + target: active, + currentTarget: active, + preventDefault: $.noop + }); + }, + + _findActive: function( selector ) { + return typeof selector === "number" ? this.headers.eq( selector ) : $(); + }, + + _setupEvents: function( event ) { + var events = { + keydown: "_keydown" + }; + if ( event ) { + $.each( event.split( " " ), function( index, eventName ) { + events[ eventName ] = "_eventHandler"; + }); + } + + this._off( this.headers.add( this.headers.next() ) ); + this._on( this.headers, events ); + this._on( this.headers.next(), { keydown: "_panelKeyDown" }); + this._hoverable( this.headers ); + this._focusable( this.headers ); + }, + + _eventHandler: function( event ) { + var options = this.options, + active = this.active, + clicked = $( event.currentTarget ), + clickedIsActive = clicked[ 0 ] === active[ 0 ], + collapsing = clickedIsActive && options.collapsible, + toShow = collapsing ? $() : clicked.next(), + toHide = active.next(), + eventData = { + oldHeader: active, + oldPanel: toHide, + newHeader: collapsing ? $() : clicked, + newPanel: toShow + }; + + event.preventDefault(); + + if ( + // click on active header, but not collapsible + ( clickedIsActive && !options.collapsible ) || + // allow canceling activation + ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { + return; + } + + options.active = collapsing ? false : this.headers.index( clicked ); + + // when the call to ._toggle() comes after the class changes + // it causes a very odd bug in IE 8 (see #6720) + this.active = clickedIsActive ? $() : clicked; + this._toggle( eventData ); + + // switch classes + // corner classes on the previously active header stay after the animation + active.removeClass( "ui-accordion-header-active ui-state-active" ); + if ( options.icons ) { + active.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.activeHeader ) + .addClass( options.icons.header ); + } + + if ( !clickedIsActive ) { + clicked + .removeClass( "ui-corner-all" ) + .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ); + if ( options.icons ) { + clicked.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.header ) + .addClass( options.icons.activeHeader ); + } + + clicked + .next() + .addClass( "ui-accordion-content-active" ); + } + }, + + _toggle: function( data ) { + var toShow = data.newPanel, + toHide = this.prevShow.length ? this.prevShow : data.oldPanel; + + // handle activating a panel during the animation for another activation + this.prevShow.add( this.prevHide ).stop( true, true ); + this.prevShow = toShow; + this.prevHide = toHide; + + if ( this.options.animate ) { + this._animate( toShow, toHide, data ); + } else { + toHide.hide(); + toShow.show(); + this._toggleComplete( data ); + } + + toHide.attr({ + "aria-hidden": "true" + }); + toHide.prev().attr({ + "aria-selected": "false", + "aria-expanded": "false" + }); + // if we're switching panels, remove the old header from the tab order + // if we're opening from collapsed state, remove the previous header from the tab order + // if we're collapsing, then keep the collapsing header in the tab order + if ( toShow.length && toHide.length ) { + toHide.prev().attr({ + "tabIndex": -1, + "aria-expanded": "false" + }); + } else if ( toShow.length ) { + this.headers.filter(function() { + return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0; + }) + .attr( "tabIndex", -1 ); + } + + toShow + .attr( "aria-hidden", "false" ) + .prev() + .attr({ + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + }); + }, + + _animate: function( toShow, toHide, data ) { + var total, easing, duration, + that = this, + adjust = 0, + boxSizing = toShow.css( "box-sizing" ), + down = toShow.length && + ( !toHide.length || ( toShow.index() < toHide.index() ) ), + animate = this.options.animate || {}, + options = down && animate.down || animate, + complete = function() { + that._toggleComplete( data ); + }; + + if ( typeof options === "number" ) { + duration = options; + } + if ( typeof options === "string" ) { + easing = options; + } + // fall back from options to animation in case of partial down settings + easing = easing || options.easing || animate.easing; + duration = duration || options.duration || animate.duration; + + if ( !toHide.length ) { + return toShow.animate( this.showProps, duration, easing, complete ); + } + if ( !toShow.length ) { + return toHide.animate( this.hideProps, duration, easing, complete ); + } + + total = toShow.show().outerHeight(); + toHide.animate( this.hideProps, { + duration: duration, + easing: easing, + step: function( now, fx ) { + fx.now = Math.round( now ); + } + }); + toShow + .hide() + .animate( this.showProps, { + duration: duration, + easing: easing, + complete: complete, + step: function( now, fx ) { + fx.now = Math.round( now ); + if ( fx.prop !== "height" ) { + if ( boxSizing === "content-box" ) { + adjust += fx.now; + } + } else if ( that.options.heightStyle !== "content" ) { + fx.now = Math.round( total - toHide.outerHeight() - adjust ); + adjust = 0; + } + } + }); + }, + + _toggleComplete: function( data ) { + var toHide = data.oldPanel; + + toHide + .removeClass( "ui-accordion-content-active" ) + .prev() + .removeClass( "ui-corner-top" ) + .addClass( "ui-corner-all" ); + + // Work around for rendering bug in IE (#5421) + if ( toHide.length ) { + toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className; + } + this._trigger( "activate", null, data ); + } +}); + + +/*! + * jQuery UI Menu 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license * - * Depends: - * jquery.effects.core.js + * http://api.jqueryui.com/menu/ */ -(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e== -"show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Explode 1.8.14 + + +var menu = $.widget( "ui.menu", { + version: "1.11.4", + defaultElement: "