diff --git a/ambari-server/src/main/java/org/apache/ambari/annotations/ExperimentalFeature.java b/ambari-server/src/main/java/org/apache/ambari/annotations/ExperimentalFeature.java index 35c3c2fe419..94be1774267 100644 --- a/ambari-server/src/main/java/org/apache/ambari/annotations/ExperimentalFeature.java +++ b/ambari-server/src/main/java/org/apache/ambari/annotations/ExperimentalFeature.java @@ -45,5 +45,11 @@ public enum ExperimentalFeature { /** * For code that is for multi-service */ - MULTI_SERVICE + MULTI_SERVICE, + + /** + * For code that is for upgrading Mpacks. Use this to mark code that may ultimately + * be removed. + */ + MPACK_UPGRADES } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java index befa31bc2e3..7525e6fe3b9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java @@ -74,6 +74,7 @@ import org.apache.ambari.server.state.stack.upgrade.HostOrderGrouping; import org.apache.ambari.server.state.stack.upgrade.HostOrderItem; import org.apache.ambari.server.state.stack.upgrade.HostOrderItem.HostOrderActionType; +import org.apache.ambari.server.state.stack.upgrade.Lifecycle; import org.apache.ambari.server.state.stack.upgrade.UpgradeScope; import org.apache.ambari.server.state.stack.upgrade.UpgradeType; import org.apache.commons.collections.CollectionUtils; @@ -1296,7 +1297,7 @@ void check(Cluster cluster, Direction direction, UpgradeType type, UpgradePack u // verify that the upgradepack has the required grouping and set the // action items on it HostOrderGrouping hostOrderGrouping = null; - List groupings = upgradePack.getGroups(direction); + List groupings = upgradePack.getGroups(Lifecycle.LifecycleType.UPGRADE, direction); for (Grouping grouping : groupings) { if (grouping instanceof HostOrderGrouping) { hostOrderGrouping = (HostOrderGrouping) grouping; diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java index 0af68400c10..bad222ce2ff 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java @@ -61,6 +61,7 @@ import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent; import org.apache.ambari.server.state.stack.upgrade.Direction; import org.apache.ambari.server.state.stack.upgrade.Grouping; +import org.apache.ambari.server.state.stack.upgrade.Lifecycle; import org.apache.ambari.server.state.stack.upgrade.ManualTask; import org.apache.ambari.server.state.stack.upgrade.RestartTask; import org.apache.ambari.server.state.stack.upgrade.ServiceCheckGrouping; @@ -301,7 +302,7 @@ public List createSequence(UpgradePack upgradePack, List groups = new ArrayList<>(); UpgradeGroupHolder previousGroupHolder = null; - for (Grouping group : upgradePack.getGroups(context.getDirection())) { + for (Grouping group : upgradePack.getGroups(Lifecycle.LifecycleType.UPGRADE, context.getDirection())) { // !!! grouping is not scoped to context if (!context.isScoped(group.scope)) { diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java index 7fb04f5f13b..c69df9e7cb5 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java @@ -18,11 +18,13 @@ package org.apache.ambari.server.state.stack; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.regex.Pattern; import javax.xml.bind.Unmarshaller; @@ -35,12 +37,15 @@ import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlValue; +import org.apache.ambari.annotations.Experimental; +import org.apache.ambari.annotations.ExperimentalFeature; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.state.stack.upgrade.ClusterGrouping; import org.apache.ambari.server.state.stack.upgrade.ConfigureTask; import org.apache.ambari.server.state.stack.upgrade.CreateAndConfigureTask; import org.apache.ambari.server.state.stack.upgrade.Direction; import org.apache.ambari.server.state.stack.upgrade.Grouping; +import org.apache.ambari.server.state.stack.upgrade.Lifecycle; import org.apache.ambari.server.state.stack.upgrade.ServiceCheckGrouping; import org.apache.ambari.server.state.stack.upgrade.Task; import org.apache.ambari.server.state.stack.upgrade.UpgradeType; @@ -55,8 +60,6 @@ @XmlAccessorType(XmlAccessType.FIELD) public class UpgradePack { - private static final String ALL_VERSIONS = "*"; - private static final Logger LOG = LoggerFactory.getLogger(UpgradePack.class); /** @@ -70,9 +73,8 @@ public class UpgradePack { @XmlElement(name="target-stack") private String targetStack; - @XmlElementWrapper(name="order") - @XmlElement(name="group") - private List groups; + @XmlElement(name="lifecycle") + public List lifecycles; @XmlElement(name="prerequisite-checks") private PrerequisiteChecks prerequisiteChecks; @@ -152,8 +154,8 @@ public UpgradeType getType() { */ public List getPrerequisiteChecks() { if (prerequisiteChecks == null) { - return new ArrayList(); - } + return new ArrayList<>(); + } return new ArrayList<>(prerequisiteChecks.checks); } @@ -164,7 +166,7 @@ public List getPrerequisiteChecks() { public PrerequisiteCheckConfig getPrerequisiteCheckConfig() { if (prerequisiteChecks == null) { return new PrerequisiteCheckConfig(); - } + } return prerequisiteChecks.configuration; } @@ -281,8 +283,16 @@ public boolean isServiceCheckFailureAutoSkipped() { return skipServiceCheckFailures; } + /** + * Used to get all groups defined for an upgrade. This method is deprecated as orchestration + * will change to per-lifecycle. At the time of writing, keep this for compilation purposes. + * @return + */ + @Experimental(feature = ExperimentalFeature.MPACK_UPGRADES) + @Deprecated public List getAllGroups() { - return groups; + + return Collections.emptyList(); } /** @@ -293,19 +303,28 @@ public List getAllGroups() { * the direction to return the ordered groups * @return the list of groups */ - public List getGroups(Direction direction) { - List list = new ArrayList<>(); + public List getGroups(Lifecycle.LifecycleType type, Direction direction) { + + // !!! lifecycles are bound to be only one per-type per-Upgrade Pack, so findFirst() is ok here + Optional optional = lifecycles.stream().filter(l -> l.type == type).findFirst(); + if (!optional.isPresent()) { + return Collections.emptyList(); + } + + List list; + List groups = optional.get().groups; + if (direction.isUpgrade()) { list = groups; } else { - switch (type) { + switch (this.type) { case NON_ROLLING: - list = getDowngradeGroupsForNonrolling(); + list = getDowngradeGroupsForNonrolling(groups); break; case HOST_ORDERED: case ROLLING: default: - list = getDowngradeGroupsForRolling(); + list = getDowngradeGroupsForRolling(groups); break; } } @@ -369,7 +388,7 @@ public boolean canBeApplied(String targetVersion){ * * @return the list of groups, reversed appropriately for a downgrade. */ - private List getDowngradeGroupsForRolling() { + private List getDowngradeGroupsForRolling(List groups) { List reverse = new ArrayList<>(); // !!! Testing exposed groups.size() == 1 issue. Normally there's no precedent for @@ -405,7 +424,7 @@ private List getDowngradeGroupsForRolling() { return reverse; } - private List getDowngradeGroupsForNonrolling() { + private List getDowngradeGroupsForNonrolling(List groups) { List list = new ArrayList<>(); for (Grouping g : groups) { list.add(g); @@ -477,8 +496,10 @@ private void initializeProcessingComponentMappings() { * @return {@code true} if the upgrade targets any version or stack. Both * {@link #target} and {@link #targetStack} must equal "*" */ + @Deprecated + @Experimental(feature=ExperimentalFeature.MPACK_UPGRADES) public boolean isAllTarget() { - return ALL_VERSIONS.equals(target) && ALL_VERSIONS.equals(targetStack); + return false; } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Lifecycle.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Lifecycle.java new file mode 100644 index 00000000000..3d75892ff0e --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Lifecycle.java @@ -0,0 +1,133 @@ +/* + * 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.ambari.server.state.stack.upgrade; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlEnumValue; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * A lifecycle is used to delineate specific portions of an upgrade. Orchestration + * will occur based on the lifecycle phases in order they are declared in {@link LifecycleType}, + * namely: + * + *
    + *
  1. INSTALL
  2. + *
  3. QUIET
  4. + *
  5. SNAPSHOT
  6. + *
  7. PREPARE
  8. + *
  9. STOP
  10. + *
  11. UPGRADE
  12. + *
  13. START
  14. + *
  15. FINALIZE
  16. + *
+ */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement +public class Lifecycle { + + @XmlAttribute + public LifecycleType type; + + @XmlElementWrapper(name="order") + @XmlElement(name="group") + public List groups; + + /** + * Identifies the lifecycle types + */ + @XmlEnum + public enum LifecycleType { + + /** + * Work required that can be classified as installation. Normally installation of + * bits occurs outside the scope of upgrade orchestration. + */ + @XmlEnumValue("install") + INSTALL(1.0f), + + /** + * Work to stop and wait on, for example, queues or topologies. + */ + @XmlEnumValue("quiet") + QUIET(2.0f), + + /** + * Work required to snapshot or other backup. + */ + @XmlEnumValue("snapshot") + SNAPSHOT(3.0f), + + /** + * Work required to prepare to upgrade. + */ + @XmlEnumValue("prepare") + PREPARE(4.0f), + + /** + * Work required to stop a service. + */ + @XmlEnumValue("stop") + STOP(5.0f), + + /** + * For a Rolling upgrade, work required to restart and upgrade the service. + */ + @XmlEnumValue("upgrade") + UPGRADE(6.0f), + + /** + * Work required to start a service. + */ + @XmlEnumValue("start") + START(7.0f), + + /** + * Work required to finalize. Will not happen until the end of the upgrade. + */ + @XmlEnumValue("finalize") + FINALIZE(8.0f); + + private float m_order; + + private LifecycleType(float order) { + m_order = order; + } + + + /** + * Returns the ordered collection of lifecycle types. This is prefered over {@link #values()} + * to preserve ordering when adding new values. + */ + public static Collection ordered() { + return Stream.of(LifecycleType.values()).sorted((l1, l2) -> + Float.compare(l1.m_order, l2.m_order)).collect(Collectors.toList()); + } + } + +} diff --git a/ambari-server/src/main/resources/upgrade-pack.xsd b/ambari-server/src/main/resources/upgrade-pack.xsd index 249db232869..d005204bcf8 100644 --- a/ambari-server/src/main/resources/upgrade-pack.xsd +++ b/ambari-server/src/main/resources/upgrade-pack.xsd @@ -19,10 +19,23 @@ - This document describes the schema for an Upgrade Pack + This document describes the schema for an Upgrade Pack for a Management Pack + + + + + + + + + + + + + @@ -272,7 +285,7 @@ - + @@ -410,18 +423,6 @@ - - - - - - - - - - - - @@ -438,15 +439,21 @@ - - - - - Ensures that the element "processing" does not have duplicate services - - - - + + + + + + + Ensures that the element "processing" does not have duplicate services + + + + + + + + @@ -466,6 +473,12 @@ + + + + + + diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java new file mode 100644 index 00000000000..24c03797c4a --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java @@ -0,0 +1,74 @@ +/** + * 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.ambari.server.state.stack; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.apache.ambari.server.stack.ModuleFileUnmarshaller; +import org.apache.ambari.server.state.stack.upgrade.Grouping; +import org.apache.ambari.server.state.stack.upgrade.Lifecycle; +import org.apache.ambari.server.state.stack.upgrade.Lifecycle.LifecycleType; +import org.junit.Test; + +/** + * Tests for the upgrade pack + */ +public class UpgradePackTest { + + @Test + public void testLifecycleOrdering() throws Exception { + Collection typeSet = LifecycleType.ordered(); + + LifecycleType[] types = typeSet.toArray(new LifecycleType[typeSet.size()]); + + assertEquals(LifecycleType.INSTALL, types[0]); + assertEquals(LifecycleType.FINALIZE, types[types.length-1]); + } + + @Test + public void testUpgradeParsing() throws Exception { + File f = new File("src/test/resources/mpacks-v2/upgrade-packs/upgrade-basic.xml"); + + ModuleFileUnmarshaller unmarshaller = new ModuleFileUnmarshaller(); + + UpgradePack upgradepack = unmarshaller.unmarshal(UpgradePack.class, f, true); + + assertEquals(6, upgradepack.lifecycles.size()); + + Optional upgradeLifecycle = upgradepack.lifecycles.stream() + .filter(l -> l.type == LifecycleType.UPGRADE).findFirst(); + + Optional startLifecycle = upgradepack.lifecycles.stream() + .filter(l -> l.type == LifecycleType.START).findFirst(); + + assertTrue(upgradeLifecycle.isPresent()); + assertFalse(startLifecycle.isPresent()); + + List groups = upgradeLifecycle.get().groups; + + assertEquals(29, groups.size()); + } + +} diff --git a/ambari-server/src/test/resources/mpacks-v2/upgrade-packs/upgrade-basic.xml b/ambari-server/src/test/resources/mpacks-v2/upgrade-packs/upgrade-basic.xml new file mode 100644 index 00000000000..83efb0eb8ad --- /dev/null +++ b/ambari-server/src/test/resources/mpacks-v2/upgrade-packs/upgrade-basic.xml @@ -0,0 +1,1147 @@ + + + + + 2.6.*.* + HDP-2.6 + false + false + ROLLING + + + org.apache.ambari.server.checks.HiveMultipleMetastoreCheck + org.apache.ambari.server.checks.MapReduce2JobHistoryStatePreservingCheck + org.apache.ambari.server.checks.SecondaryNamenodeDeletedCheck + org.apache.ambari.server.checks.ServicesMapReduceDistributedCacheCheck + org.apache.ambari.server.checks.ServicesNamenodeHighAvailabilityCheck + org.apache.ambari.server.checks.ServicesTezDistributedCacheCheck + org.apache.ambari.server.checks.ServicesYarnWorkPreservingCheck + org.apache.ambari.server.checks.YarnRMHighAvailabilityCheck + org.apache.ambari.server.checks.YarnTimelineServerStatePreservingCheck + org.apache.ambari.server.checks.DruidHighAvailabilityCheck + org.apache.ambari.server.checks.LZOCheck + + + + + + HDP-2.3.0.0 + + + + + + + + + + + + + + + + + + + + UPGRADE + + + + prepare_rolling_upgrade + + + + + + + prepare + + + + + + + + + + UPGRADE + true + false + + + + + take_snapshot + + + + + + Before continuing, please backup the Hive Metastore database referenced by the Hive Metastore service(s) located on the following host(s): {{hosts.all}}. + + + + + + Before continuing, please backup the Oozie Server database referenced by the Oozie server located on {{hosts.all}}. + + + + + + Before continuing, please backup the Ranger Admin database on the following host(s): {{hosts.all}}. + + + + + + Before continuing, please backup Ranger KMS database on the following host(s): {{hosts.all}}. + + + + + Before continuing, please backup Zeppelin notebooks directory on the following host(s): {{hosts.all}}. + + + + + + + + + + false + + ZOOKEEPER_SERVER + + + + + true + false + + RANGER_ADMIN + RANGER_USERSYNC + RANGER_TAGSYNC + + + + + true + false + + RANGER_KMS_SERVER + + + + + + true + + KAFKA_BROKER + + + + + + + + + + + + + false + + NAMENODE + JOURNALNODE + ZKFC + + + + HISTORYSERVER + + + + APP_TIMELINE_SERVER + RESOURCEMANAGER + + + + HBASE_MASTER + + + + + UPGRADE + true + + ZOOKEEPER + HDFS + YARN + MAPREDUCE2 + HBASE + + + AMBARI_METRICS + LOGSEARCH + + + + + false + true + + DATANODE + NFS_GATEWAY + + + + HBASE_REGIONSERVER + PHOENIX_QUERY_SERVER + + + + NODEMANAGER + + + + 20 + Verification Required + The initial batch of {{components}} hosts have been {{direction.past}}. You are advised to check the hosts and perform cluster/workload-specific tests against your cluster to ensure proper operation before proceeding with {{direction.text}} of the remaining services. + + + + + UPGRADE + true + + ZOOKEEPER + HDFS + YARN + HBASE + + + AMBARI_METRICS + LOGSEARCH + + + + + + true + + SLIDER + + + + + true + false + + HIVE_SERVER + HIVE_METASTORE + HIVE_SERVER_INTERACTIVE + WEBHCAT_SERVER + + + + + true + false + false + + SPARK_JOBHISTORYSERVER + SPARK_THRIFTSERVER + LIVY_SERVER + + + + + true + false + false + + SPARK2_JOBHISTORYSERVER + SPARK2_THRIFTSERVER + LIVY2_SERVER + + + + + true + false + false + + ZEPPELIN_MASTER + + + + + true + + SPARK_CLIENT + + + + + true + + SPARK2_CLIENT + + + + + false + true + false + + ATLAS_SERVER + + + + + false + true + true + + ATLAS_CLIENT + + + + + true + false + false + + OOZIE_SERVER + + + + + true + + OOZIE_CLIENT + + + + + true + false + false + + FALCON_SERVER + + + + + true + + FALCON_CLIENT + + + + + false + + ZOOKEEPER_CLIENT + + + HDFS_CLIENT + + + + YARN_CLIENT + + + + MAPREDUCE2_CLIENT + + + + TEZ_CLIENT + + + + HBASE_CLIENT + + + + PIG + + + + SQOOP + + + + MAHOUT + + + + HIVE_CLIENT + HCAT + + + + + true + false + false + + DRUID_HISTORICAL + DRUID_MIDDLEMANAGER + DRUID_BROKER + DRUID_ROUTER + DRUID_COORDINATOR + DRUID_OVERLORD + DRUID_SUPERSET + + + + + UPGRADE + true + + ZOOKEEPER + HDFS + YARN + HBASE + DRUID + + + AMBARI_METRICS + LOGSEARCH + + + + + true + + KNOX_GATEWAY + + + + + true + + NIMBUS + SUPERVISOR + STORM_UI_SERVER + DRPC_SERVER + + + + + true + + FLUME_HANDLER + + + + + true + + ACCUMULO_MASTER + ACCUMULO_TSERVER + ACCUMULO_MONITOR + ACCUMULO_GC + ACCUMULO_TRACER + ACCUMULO_CLIENT + + + + + + + + + COMPLETE + + + + actionexecute + + + + + + UPGRADE + + + + + + + + true + false + + + UPGRADE + + Please confirm you are ready to finalize. + + + + + UPGRADE + + You can now remove any HBase snapshots which were created at the beginning of the upgrade. To see existing snapshots, use the following HBase shell command: + hbase> list_snapshots + Once you have found an existing snapshot which you would like to remove, you can use the following command: + hbase> delete_snapshot 'snapshotName' + + + + + + + finalize_rolling_upgrade + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Stop Ranger Admin + + stop + + + + + set_pre_start + + + + Upgrading Ranger database schema + + setup_ranger_database + + + + + + Applying Ranger java patches + + setup_ranger_java_patches + + + + + + + + + + + + + + + Enabling Nested Group Sync for Ranger + + + + + + + + + + + + + + + + + + + + + + + Upgrading Ranger KMS database schema + + setup_ranger_kms_database + + + + + + Downgrading Ranger KMS database schema + + setup_ranger_kms_database + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Verifying LZO codec path for mapreduce + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Verifying LZO codec path for Tez + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shut down all Oozie servers + + stop + + + + Adjusting Oozie properties + + + + + + + Upgrading the Oozie database and creating a new sharelib + + upgrade_oozie_database_and_sharelib + + + + + + Shut down all Oozie servers + + stop + + + + Create a new sharelib + + create_sharelib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Applying Atlas proxy-user configurations. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +