diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/Device.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/Device.java new file mode 100644 index 00000000000..551c2d83b33 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/Device.java @@ -0,0 +1,233 @@ +/** + * 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.hadoop.yarn.server.nodemanager.api.deviceplugin; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Represent one "device" resource. + * */ +public final class Device implements Serializable, Comparable { + + private static final long serialVersionUID = -7270474563684671656L; + + /** + * An plugin specified index number. + * Must set. Recommend starting from 0 + * */ + private final int id; + + /** + * The device node like "/dev/devname". + * Optional + * */ + private final String devPath; + + /** + * The major device number. + * Optional + * */ + private final int majorNumber; + + /** + * The minor device number. + * Optional + * */ + private final int minorNumber; + + /** + * PCI Bus ID in format [[[[]:]]:][][.[]]. + * Optional. Can get from "lspci -D" in Linux + * */ + private final String busID; + + /** + * Is healthy or not. + * false by default + * */ + private boolean isHealthy; + + /** + * Plugin customized status info. + * Optional + * */ + private String status; + + /** + * Private constructor. + * @param builder + */ + private Device(Builder builder) { + if (builder.id == -1) { + throw new IllegalArgumentException("Please set the id for Device"); + } + this.id = builder.id; + this.devPath = builder.devPath; + this.majorNumber = builder.majorNumber; + this.minorNumber = builder.minorNumber; + this.busID = builder.busID; + this.isHealthy = builder.isHealthy; + this.status = builder.status; + } + + public int getId() { + return id; + } + + public String getDevPath() { + return devPath; + } + + public int getMajorNumber() { + return majorNumber; + } + + public int getMinorNumber() { + return minorNumber; + } + + public String getBusID() { + return busID; + } + + public boolean isHealthy() { + return isHealthy; + } + + public String getStatus() { + return status; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Device device = (Device) o; + return id == device.getId() + && Objects.equals(devPath, device.getDevPath()) + && majorNumber == device.getMajorNumber() + && minorNumber == device.getMinorNumber() + && Objects.equals(busID, device.getBusID()); + } + + @Override + public int hashCode() { + return Objects.hash(id, devPath, majorNumber, minorNumber, busID); + } + + @Override + public int compareTo(Object o) { + if (o == null || (!(o instanceof Device))) { + return -1; + } + + Device other = (Device) o; + + int result = Integer.compare(id, other.getId()); + if (0 != result) { + return result; + } + + result = Integer.compare(majorNumber, other.getMajorNumber()); + if (0 != result) { + return result; + } + + result = Integer.compare(minorNumber, other.getMinorNumber()); + if (0 != result) { + return result; + } + + result = devPath.compareTo(other.getDevPath()); + if (0 != result) { + return result; + } + + return busID.compareTo(other.getBusID()); + } + + @Override + public String toString() { + return "(" + getId() + ", " + getDevPath() + ", " + + getMajorNumber() + ":" + getMinorNumber() + ")"; + } + + /** + * Builder for Device. + * */ + public final static class Builder { + // default -1 representing the value is not set + private int id = -1; + private String devPath = ""; + private int majorNumber; + private int minorNumber; + private String busID = ""; + private boolean isHealthy; + private String status = ""; + + public static Builder newInstance() { + return new Builder(); + } + + public Device build() { + return new Device(this); + } + + public Builder setId(int i) { + this.id = i; + return this; + } + + public Builder setDevPath(String dp) { + this.devPath = dp; + return this; + } + + public Builder setMajorNumber(int maN) { + this.majorNumber = maN; + return this; + } + + public Builder setMinorNumber(int miN) { + this.minorNumber = miN; + return this; + } + + public Builder setBusID(String bI) { + this.busID = bI; + return this; + } + + public Builder setHealthy(boolean healthy) { + isHealthy = healthy; + return this; + } + + public Builder setStatus(String s) { + this.status = s; + return this; + } + + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DevicePlugin.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DevicePlugin.java new file mode 100644 index 00000000000..5b7a5697f57 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DevicePlugin.java @@ -0,0 +1,65 @@ +/** + * 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.hadoop.yarn.server.nodemanager.api.deviceplugin; + +import java.util.Set; + +/** + * A must interface for vendor plugin to implement. + * */ +public interface DevicePlugin { + /** + * Called first when device plugin framework wants to register. + * @return DeviceRegisterRequest {@link DeviceRegisterRequest} + * */ + DeviceRegisterRequest getRegisterRequestInfo() + throws Exception; + + /** + * Called when update node resource. + * @return a set of {@link Device}, {@link java.util.TreeSet} recommended + * */ + Set getDevices() throws Exception; + + /** + * Asking how these devices should be prepared/used + * before/when container launch. A plugin can do some tasks in its own or + * define it in DeviceRuntimeSpec to let the framework do it. + * For instance, define {@code VolumeSpec} to let the + * framework to create volume before running container. + * + * @param allocatedDevices A set of allocated {@link Device}. + * @param yarnRuntime Indicate which runtime YARN will use + * Could be {@code RUNTIME_DEFAULT} or {@code RUNTIME_DOCKER} + * in {@link DeviceRuntimeSpec} constants. The default means YARN's + * non-docker container runtime is used. The docker means YARN's + * docker container runtime is used. + * @return a {@link DeviceRuntimeSpec} description about environment, + * {@link VolumeSpec}, {@link MountVolumeSpec}. etc + * */ + DeviceRuntimeSpec onDevicesAllocated(Set allocatedDevices, + YarnRuntimeType yarnRuntime) throws Exception; + + /** + * Called after device released. + * @param releasedDevices A set of released devices + * */ + void onDevicesReleased(Set releasedDevices) + throws Exception; +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DeviceRegisterRequest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DeviceRegisterRequest.java new file mode 100644 index 00000000000..8fbff88f0d6 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DeviceRegisterRequest.java @@ -0,0 +1,73 @@ +/** + * 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.hadoop.yarn.server.nodemanager.api.deviceplugin; + +import java.util.Objects; + +/** + * Contains plugin register request info. + * */ +public final class DeviceRegisterRequest { + + // plugin's own version + private final String pluginVersion; + private final String resourceName; + + private DeviceRegisterRequest(Builder builder) { + this.resourceName = Objects.requireNonNull(builder.resourceName); + this.pluginVersion = builder.pluginVersion; + } + + public String getResourceName() { + return resourceName; + } + + public String getPluginVersion() { + return pluginVersion; + } + + /** + * Builder class for construct {@link DeviceRegisterRequest}. + * */ + public final static class Builder { + private String pluginVersion; + private String resourceName; + + private Builder() {} + + public static Builder newInstance() { + return new Builder(); + } + + public DeviceRegisterRequest build() { + return new DeviceRegisterRequest(this); + } + + public Builder setResourceName(String resName) { + this.resourceName = resName; + return this; + } + + public Builder setPluginVersion(String plVersion) { + this.pluginVersion = plVersion; + return this; + } + + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DeviceRuntimeSpec.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DeviceRuntimeSpec.java new file mode 100644 index 00000000000..32bd593249b --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/DeviceRuntimeSpec.java @@ -0,0 +1,137 @@ +/** + * 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.hadoop.yarn.server.nodemanager.api.deviceplugin; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; + +/** + * This is a spec used to prepare & run container. + * It's return value of onDeviceAllocated invoked by the framework. + * For preparation, if volumeSpecs is populated then the framework will + * create the volume before using the device + * When running container, the envs indicates environment variable needed. + * The containerRuntime indicates what Docker runtime to use. + * The volume & device mounts describes key isolation requirements + * */ +public final class DeviceRuntimeSpec implements Serializable { + + private static final long serialVersionUID = 554704120015467660L; + + /** + * The containerRuntime gives device framework a hint (not forced to). + * On which containerRuntime be used + * (if empty then default "runc" is used). + * For instance, it could be "nvidia" in Nvidia GPU Docker v2. + * The "nvidia" will be passed as a parameter to docker run + * with --runtime "nvidia" + */ + private final String containerRuntime; + private final Map envs; + private final Set volumeMounts; + private final Set deviceMounts; + private final Set volumeSpecs; + + private DeviceRuntimeSpec(Builder builder) { + this.containerRuntime = builder.containerRuntime; + this.deviceMounts = builder.deviceMounts; + this.envs = builder.envs; + this.volumeSpecs = builder.volumeSpecs; + this.volumeMounts = builder.volumeMounts; + } + + public String getContainerRuntime() { + return containerRuntime; + } + + public Map getEnvs() { + return envs; + } + + public Set getVolumeMounts() { + return volumeMounts; + } + + public Set getDeviceMounts() { + return deviceMounts; + } + + public Set getVolumeSpecs() { + return volumeSpecs; + } + /** + * Builder for DeviceRuntimeSpec. + * */ + public final static class Builder { + + private String containerRuntime; + private Map envs; + private Set volumeMounts; + private Set deviceMounts; + private Set volumeSpecs; + + private Builder() { + containerRuntime = ""; + envs = new HashMap<>(); + volumeSpecs = new TreeSet<>(); + deviceMounts = new TreeSet<>(); + volumeMounts = new TreeSet<>(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public DeviceRuntimeSpec build() { + return new DeviceRuntimeSpec(this); + } + + public Builder setContainerRuntime(String cRuntime) { + this.containerRuntime = cRuntime; + return this; + } + + public Builder addVolumeSpec(VolumeSpec spec) { + this.volumeSpecs.add(spec); + return this; + } + + public Builder addMountVolumeSpec(MountVolumeSpec spec) { + this.volumeMounts.add(spec); + return this; + } + + public Builder addMountDeviceSpec(MountDeviceSpec spec) { + this.deviceMounts.add(spec); + return this; + } + + public Builder addEnv(String key, String value) { + this.envs.put(Objects.requireNonNull(key), + Objects.requireNonNull(value)); + return this; + } + + } + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/MountDeviceSpec.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/MountDeviceSpec.java new file mode 100644 index 00000000000..e4a88de4e54 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/MountDeviceSpec.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.hadoop.yarn.server.nodemanager.api.deviceplugin; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Describe one device mount. + * */ +public final class MountDeviceSpec implements Serializable, Comparable { + + private static final long serialVersionUID = -160806358136943052L; + + private final String devicePathInHost; + private final String devicePathInContainer; + + // r for only read, rw can do read and write + private final String devicePermission; + + public final static String RO = "r"; + public final static String RW = "rw"; + + private MountDeviceSpec(Builder builder) { + this.devicePathInContainer = builder.devicePathInContainer; + this.devicePathInHost = builder.devicePathInHost; + this.devicePermission = builder.devicePermission; + } + + public String getDevicePathInHost() { + return devicePathInHost; + } + + public String getDevicePathInContainer() { + return devicePathInContainer; + } + + public String getDevicePermission() { + return devicePermission; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()){ + return false; + } + MountDeviceSpec other = (MountDeviceSpec) o; + return Objects.equals(devicePathInHost, other.getDevicePathInHost()) + && Objects.equals(devicePathInContainer, + other.getDevicePathInContainer()) + && Objects.equals(devicePermission, other.getDevicePermission()); + } + + @Override + public int hashCode() { + return Objects.hash(devicePathInContainer, + devicePathInHost, devicePermission); + } + + @Override + public int compareTo(Object o) { + if (o == null || (!(o instanceof MountDeviceSpec))) { + return -1; + } + MountDeviceSpec other = (MountDeviceSpec) o; + int result = devicePathInContainer.compareTo( + other.getDevicePathInContainer()); + if (0 != result) { + return result; + } + result = devicePathInHost.compareTo(other.getDevicePathInHost()); + if (0 != result) { + return result; + } + return devicePermission.compareTo(other.getDevicePermission()); + } + + /** + * Builder for MountDeviceSpec. + * */ + public final static class Builder { + private String devicePathInHost; + private String devicePathInContainer; + private String devicePermission; + + private Builder() {} + + public static Builder newInstance() { + return new Builder(); + } + + public MountDeviceSpec build() { + return new MountDeviceSpec(this); + } + + public Builder setDevicePermission(String permission) { + this.devicePermission = permission; + return this; + } + + public Builder setDevicePathInContainer(String pathInContainer) { + this.devicePathInContainer = pathInContainer; + return this; + } + + public Builder setDevicePathInHost(String pathInHost) { + this.devicePathInHost = pathInHost; + return this; + } + } + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/MountVolumeSpec.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/MountVolumeSpec.java new file mode 100644 index 00000000000..6f6b183490d --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/MountVolumeSpec.java @@ -0,0 +1,118 @@ +/** + * 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.hadoop.yarn.server.nodemanager.api.deviceplugin; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Describe one volume mount. + * */ +public final class MountVolumeSpec implements Serializable, Comparable { + + private static final long serialVersionUID = 2479676805545997492L; + + // host path or volume name + private final String hostPath; + + // path in the container + private final String mountPath; + + // if true, data in mountPath can only be read + // "-v hostPath:mountPath:ro" + private final Boolean isReadOnly; + + public final static String READONLYOPTION = "ro"; + + private MountVolumeSpec(Builder builder) { + this.hostPath = builder.hostPath; + this.mountPath = builder.mountPath; + this.isReadOnly = builder.isReadOnly; + } + + public String getHostPath() { + return hostPath; + } + + public String getMountPath() { + return mountPath; + } + + public Boolean getReadOnly() { + return isReadOnly; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()){ + return false; + } + MountVolumeSpec other = (MountVolumeSpec) o; + return Objects.equals(hostPath, other.getHostPath()) + && Objects.equals(mountPath, other.getMountPath()) + && Objects.equals(isReadOnly, other.getReadOnly()); + } + + @Override + public int hashCode() { + return Objects.hash(hostPath, mountPath, isReadOnly); + } + + @Override + public int compareTo(Object o) { + return 0; + } + + /** + * Builder for MountVolumeSpec. + * */ + public final static class Builder { + private String hostPath; + private String mountPath; + private Boolean isReadOnly; + + private Builder() {} + + public static Builder newInstance() { + return new Builder(); + } + + public MountVolumeSpec build() { + return new MountVolumeSpec(this); + } + + public Builder setHostPath(String hPath) { + this.hostPath = hPath; + return this; + } + + public Builder setMountPath(String mPath) { + this.mountPath = mPath; + return this; + } + + public Builder setReadOnly(Boolean readOnly) { + isReadOnly = readOnly; + return this; + } + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/VolumeSpec.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/VolumeSpec.java new file mode 100644 index 00000000000..0f06be713cb --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/VolumeSpec.java @@ -0,0 +1,114 @@ +/** + * 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.hadoop.yarn.server.nodemanager.api.deviceplugin; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Describe one volume creation or deletion. + * */ +public final class VolumeSpec implements Serializable, Comparable { + + private static final long serialVersionUID = 3483619025106416736L; + + private final String volumeDriver; + private final String volumeName; + private final String volumeOperation; + + public final static String CREATE = "create"; + public final static String DELETE = "delete"; + + private VolumeSpec(Builder builder) { + this.volumeDriver = builder.volumeDriver; + this.volumeName = builder.volumeName; + this.volumeOperation = builder.volumeOperation; + } + + public String getVolumeDriver() { + return volumeDriver; + } + + public String getVolumeName() { + return volumeName; + } + + public String getVolumeOperation() { + return volumeOperation; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()){ + return false; + } + VolumeSpec other = (VolumeSpec) o; + return Objects.equals(volumeDriver, other.getVolumeDriver()) + && Objects.equals(volumeName, other.getVolumeName()) + && Objects.equals(volumeOperation, other.getVolumeOperation()); + } + + @Override + public int hashCode() { + return Objects.hash(volumeDriver, volumeName, volumeOperation); + } + + @Override + public int compareTo(Object o) { + return 0; + } + + /** + * Builder for VolumeSpec. + * */ + public final static class Builder { + private String volumeDriver; + private String volumeName; + private String volumeOperation; + + private Builder(){} + + public static Builder newInstance() { + return new Builder(); + } + + public VolumeSpec build() { + return new VolumeSpec(this); + } + + public Builder setVolumeDriver(String volDriver) { + this.volumeDriver = volDriver; + return this; + } + + public Builder setVolumeName(String volName) { + this.volumeName = volName; + return this; + } + + public Builder setVolumeOperation(String volOperation) { + this.volumeOperation = volOperation; + return this; + } + + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/YarnRuntimeType.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/YarnRuntimeType.java new file mode 100644 index 00000000000..e0eb9dd68cf --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/YarnRuntimeType.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.hadoop.yarn.server.nodemanager.api.deviceplugin; + + +/** + * YarnRuntime parameter enum for {@link DevicePlugin}. + * It's passed into {@code onDevicesAllocated}. + * Device plugin could populate {@link DeviceRuntimeSpec} + * based on which YARN container runtime will use. + * */ +public enum YarnRuntimeType { + + RUNTIME_DEFAULT("default"), + RUNTIME_DOCKER("docker"); + + private final String name; + + YarnRuntimeType(String n) { + this.name = n; + } + + public String getName() { + return name; + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/package-info.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/package-info.java new file mode 100644 index 00000000000..fd7ef24c463 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/api/deviceplugin/package-info.java @@ -0,0 +1,19 @@ +/** + * 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.hadoop.yarn.server.nodemanager.api.deviceplugin; \ No newline at end of file diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/ResourcePluginManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/ResourcePluginManager.java index e930d9665ab..9741b12ee99 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/ResourcePluginManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/ResourcePluginManager.java @@ -18,18 +18,26 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.ReflectionUtils; +import org.apache.hadoop.yarn.api.records.ResourceInformation; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; import org.apache.hadoop.yarn.server.nodemanager.Context; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DevicePlugin; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DeviceRegisterRequest; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework.DevicePluginAdapter; import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.fpga.FpgaResourcePlugin; import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.gpu.GpuResourcePlugin; +import org.apache.hadoop.yarn.util.resource.ResourceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -50,8 +58,9 @@ public class ResourcePluginManager { private Map configuredPlugins = Collections.emptyMap(); + public synchronized void initialize(Context context) - throws YarnException { + throws YarnException, ClassNotFoundException { Configuration conf = context.getConf(); Map pluginMap = new HashMap<>(); @@ -111,7 +120,7 @@ public class ResourcePluginManager { public void initializePluggableDevicePlugins(Context context, Configuration configuration, Map pluginMap) - throws YarnRuntimeException { + throws YarnRuntimeException, ClassNotFoundException { LOG.info("The pluggable device framework enabled," + "trying to load the vendor plugins"); @@ -121,6 +130,109 @@ public class ResourcePluginManager { throw new YarnRuntimeException("Null value found in configuration: " + YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_DEVICE_CLASSES); } + + for (String pluginClassName : pluginClassNames) { + Class pluginClazz = Class.forName(pluginClassName); + if (!DevicePlugin.class.isAssignableFrom(pluginClazz)) { + throw new YarnRuntimeException("Class: " + pluginClassName + + " not instance of " + DevicePlugin.class.getCanonicalName()); + } + // sanity-check before initialization + checkInterfaceCompatibility(DevicePlugin.class, pluginClazz); + + DevicePlugin dpInstance = + (DevicePlugin) ReflectionUtils.newInstance( + pluginClazz, configuration); + + // Try to register plugin + // TODO: handle the plugin method timeout issue + DeviceRegisterRequest request = null; + try { + request = dpInstance.getRegisterRequestInfo(); + } catch (Exception e) { + throw new YarnRuntimeException("Exception thrown from plugin's" + + " getRegisterRequestInfo:" + + e.getMessage()); + } + String resourceName = request.getResourceName(); + // check if someone has already registered this resource type name + if (pluginMap.containsKey(resourceName)) { + throw new YarnRuntimeException(resourceName + + " already registered! Please change resource type name" + + " or configure correct resource type name" + + " in resource-types.xml for " + + pluginClassName); + } + // check resource name is valid and configured in resource-types.xml + if (!isConfiguredResourceName(resourceName)) { + throw new YarnRuntimeException(resourceName + + " is not configured inside " + + YarnConfiguration.RESOURCE_TYPES_CONFIGURATION_FILE + + " , please configure it first"); + } + LOG.info("New resource type: {} registered successfully by {}", + resourceName, + pluginClassName); + DevicePluginAdapter pluginAdapter = new DevicePluginAdapter( + resourceName, dpInstance); + LOG.info("Adapter of {} created. Initializing..", pluginClassName); + try { + pluginAdapter.initialize(context); + } catch (YarnException e) { + throw new YarnRuntimeException("Adapter of " + + pluginClassName + " init failed!"); + } + LOG.info("Adapter of {} init success!", pluginClassName); + // Store plugin as adapter instance + pluginMap.put(request.getResourceName(), pluginAdapter); + } // end for + } + + @VisibleForTesting + // Check if the implemented interfaces' signature is compatible + public void checkInterfaceCompatibility(Class expectedClass, + Class actualClass) throws YarnRuntimeException{ + LOG.debug("Checking implemented interface's compatibility: {}", + expectedClass.getSimpleName()); + Method[] expectedDevicePluginMethods = expectedClass.getMethods(); + + // Check method compatibility + boolean found; + for (Method method: expectedDevicePluginMethods) { + found = false; + LOG.debug("Try to find method: {}", + method.getName()); + for (Method m : actualClass.getDeclaredMethods()) { + if (m.getName().equals(method.getName())) { + LOG.debug("Method {} found in class {}", + actualClass.getSimpleName(), + m.getName()); + found = true; + break; + } + } + if (!found) { + LOG.error("Method {} is not found in plugin", + method.getName()); + throw new YarnRuntimeException( + "Method " + method.getName() + + " is expected but not implemented in " + + actualClass.getCanonicalName()); + } + }// end for + LOG.info("{} compatibility is ok.", + expectedClass.getSimpleName()); + } + + @VisibleForTesting + public boolean isConfiguredResourceName(String resourceName) { + // check configured + Map configuredResourceTypes = + ResourceUtils.getResourceTypes(); + if (!configuredResourceTypes.containsKey(resourceName)) { + return false; + } + return true; } public synchronized void cleanup() throws YarnException { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/DevicePluginAdapter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/DevicePluginAdapter.java new file mode 100644 index 00000000000..18a6992b062 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/DevicePluginAdapter.java @@ -0,0 +1,88 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.server.nodemanager.Context; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DevicePlugin; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.CGroupsHandler; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandler; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.DockerCommandPlugin; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.NodeResourceUpdaterPlugin; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.ResourcePlugin; +import org.apache.hadoop.yarn.server.nodemanager.webapp.dao.NMResourceInfo; + + +/** + * The {@link DevicePluginAdapter} will adapt existing hooks + * into vendor plugin's logic. + * It decouples the vendor plugin from YARN's device framework + * + * */ +public class DevicePluginAdapter implements ResourcePlugin { + private final static Log LOG = LogFactory.getLog(DevicePluginAdapter.class); + + private final String resourceName; + private final DevicePlugin devicePlugin; + private DeviceResourceUpdaterImpl deviceResourceUpdater; + + public DevicePluginAdapter(String name, DevicePlugin dp) { + resourceName = name; + devicePlugin = dp; + } + + @Override + public void initialize(Context context) throws YarnException { + deviceResourceUpdater = new DeviceResourceUpdaterImpl( + resourceName, devicePlugin); + LOG.info(resourceName + " plugin adapter initialized"); + return; + } + + @Override + public ResourceHandler createResourceHandler(Context nmContext, + CGroupsHandler cGroupsHandler, + PrivilegedOperationExecutor privilegedOperationExecutor) { + return null; + } + + @Override + public NodeResourceUpdaterPlugin getNodeResourceHandlerInstance() { + return deviceResourceUpdater; + } + + @Override + public void cleanup() { + + } + + @Override + public DockerCommandPlugin getDockerCommandPluginInstance() { + return null; + } + + @Override + public NMResourceInfo getNMResourceInfo() throws YarnException { + return null; + } + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/DeviceResourceUpdaterImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/DeviceResourceUpdaterImpl.java new file mode 100644 index 00000000000..e5ef5780581 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/DeviceResourceUpdaterImpl.java @@ -0,0 +1,66 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.yarn.api.records.Resource; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.Device; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DevicePlugin; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.NodeResourceUpdaterPlugin; + +import java.util.Set; + +/** + * Hooks into NodeStatusUpdater to update resource. + * */ +public class DeviceResourceUpdaterImpl extends NodeResourceUpdaterPlugin { + + final static Log LOG = LogFactory.getLog(DeviceResourceUpdaterImpl.class); + + private String resourceName; + private DevicePlugin devicePlugin; + + public DeviceResourceUpdaterImpl(String resourceName, + DevicePlugin devicePlugin) { + this.devicePlugin = devicePlugin; + this.resourceName = resourceName; + } + + @Override + public void updateConfiguredResource(Resource res) + throws YarnException { + LOG.info(resourceName + " plugin update resource "); + Set devices = null; + try { + devices = devicePlugin.getDevices(); + } catch (Exception e) { + throw new YarnException("Exception thrown from plugin's getDevices" + + e.getMessage()); + } + if (null == devices) { + LOG.warn(resourceName + + " plugin failed to discover resource ( null value got)."); + return; + } + res.setResourceValue(resourceName, devices.size()); + } + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/package-info.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/package-info.java new file mode 100644 index 00000000000..c64fa951a15 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/package-info.java @@ -0,0 +1,19 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; \ No newline at end of file diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/package-info.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/package-info.java new file mode 100644 index 00000000000..b4be24978ec --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/package-info.java @@ -0,0 +1,19 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin; \ No newline at end of file diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/TestResourcePluginManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/TestResourcePluginManager.java index eb8bf542e9e..f6233754c63 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/TestResourcePluginManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/TestResourcePluginManager.java @@ -34,6 +34,7 @@ import org.apache.hadoop.yarn.server.nodemanager.NodeHealthCheckerService; import org.apache.hadoop.yarn.server.nodemanager.NodeManager; import org.apache.hadoop.yarn.server.nodemanager.NodeManagerTestBase; import org.apache.hadoop.yarn.server.nodemanager.NodeStatusUpdater; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DevicePlugin; import org.apache.hadoop.yarn.server.nodemanager.containermanager.ContainerManagerImpl; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation; @@ -42,7 +43,10 @@ import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resource import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandler; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandlerChain; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandlerException; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework.*; import org.apache.hadoop.yarn.server.security.ApplicationACLsManager; +import org.apache.hadoop.yarn.util.resource.ResourceUtils; +import org.apache.hadoop.yarn.util.resource.TestResourceUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -51,6 +55,7 @@ import org.junit.Test; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.io.File; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; @@ -64,9 +69,16 @@ public class TestResourcePluginManager extends NodeManagerTestBase { private YarnConfiguration conf; + private String tempResourceTypesFile; + @Before public void setup() throws Exception { this.conf = createNMConfig(); + // setup resource-types.xml + ResourceUtils.resetResourceTypes(); + String resourceTypesFile = "resource-types-pluggable-devices.xml"; + this.tempResourceTypesFile = + TestResourceUtils.setupResourceTypes(this.conf, resourceTypesFile); } ResourcePluginManager stubResourcePluginmanager() { @@ -101,6 +113,11 @@ public class TestResourcePluginManager extends NodeManagerTestBase { // ignore } } + // cleanup resource-types.xml + File dest = new File(this.tempResourceTypesFile); + if (dest.exists()) { + dest.delete(); + } } private class CustomizedResourceHandler implements ResourceHandler { @@ -153,8 +170,8 @@ public class TestResourcePluginManager extends NodeManagerTestBase { protected NodeStatusUpdater createNodeStatusUpdater(Context context, Dispatcher dispatcher, NodeHealthCheckerService healthChecker) { ((NodeManager.NMContext)context).setResourcePluginManager(rpm); - return new BaseNodeStatusUpdaterForTest(context, dispatcher, healthChecker, - metrics, new BaseResourceTrackerForTest()); + return new BaseNodeStatusUpdaterForTest(context, dispatcher, + healthChecker, metrics, new BaseResourceTrackerForTest()); } @Override @@ -174,7 +191,8 @@ public class TestResourcePluginManager extends NodeManagerTestBase { } public class MyLCE extends LinuxContainerExecutor { - private PrivilegedOperationExecutor poe = mock(PrivilegedOperationExecutor.class); + private PrivilegedOperationExecutor poe = + mock(PrivilegedOperationExecutor.class); @Override protected PrivilegedOperationExecutor getPrivilegedOperationExecutor() { @@ -199,7 +217,8 @@ public class TestResourcePluginManager extends NodeManagerTestBase { * Make sure ResourcePluginManager is invoked during NM update. */ @Test(timeout = 30000) - public void testNodeStatusUpdaterWithResourcePluginsEnabled() throws Exception { + public void testNodeStatusUpdaterWithResourcePluginsEnabled() + throws Exception { final ResourcePluginManager rpm = stubResourcePluginmanager(); nm = new MyMockNM(rpm); @@ -211,15 +230,16 @@ public class TestResourcePluginManager extends NodeManagerTestBase { rpm.getNameToPlugins().get("resource1") .getNodeResourceHandlerInstance(); - verify(nodeResourceUpdaterPlugin, times(1)).updateConfiguredResource( - any(Resource.class)); + verify(nodeResourceUpdaterPlugin, times(1)) + .updateConfiguredResource(any(Resource.class)); } /* * Make sure ResourcePluginManager is used to initialize ResourceHandlerChain */ @Test(timeout = 30000) - public void testLinuxContainerExecutorWithResourcePluginsEnabled() throws Exception { + public void testLinuxContainerExecutorWithResourcePluginsEnabled() + throws Exception { final ResourcePluginManager rpm = stubResourcePluginmanager(); final LinuxContainerExecutor lce = new MyLCE(); @@ -261,6 +281,9 @@ public class TestResourcePluginManager extends NodeManagerTestBase { boolean newHandlerAdded = false; for (ResourceHandler h : ((ResourceHandlerChain) handler) .getResourceHandlerList()) { + if (h instanceof DevicePluginAdapter) { + Assert.assertTrue(false); + } if (h instanceof CustomizedResourceHandler) { newHandlerAdded = true; break; @@ -320,7 +343,7 @@ public class TestResourcePluginManager extends NodeManagerTestBase { true); conf.setStrings( YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_DEVICE_CLASSES, - "com.cmp1.hdw1plugin"); + FakeTestDevicePlugin1.class.getCanonicalName()); nm.init(conf); nm.start(); verify(rpmSpy, times(1)).initialize( @@ -332,7 +355,8 @@ public class TestResourcePluginManager extends NodeManagerTestBase { // Enable pluggable framework, but leave device classes un-configured // initializePluggableDevicePlugins invoked but it should throw an exception @Test(timeout = 30000) - public void testInitializationWithPluggableDeviceFrameworkEnabled2() { + public void testInitializationWithPluggableDeviceFrameworkEnabled2() + throws ClassNotFoundException { ResourcePluginManager rpm = new ResourcePluginManager(); ResourcePluginManager rpmSpy = spy(rpm); @@ -353,4 +377,131 @@ public class TestResourcePluginManager extends NodeManagerTestBase { any(Context.class), any(Configuration.class), any(Map.class)); Assert.assertTrue(fail); } + + @Test(timeout = 30000) + public void testNormalInitializationOfPluggableDeviceClasses() + throws Exception { + + ResourcePluginManager rpm = new ResourcePluginManager(); + + ResourcePluginManager rpmSpy = spy(rpm); + nm = new MyMockNM(rpmSpy); + + conf.setBoolean(YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_ENABLED, + true); + conf.setStrings( + YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_DEVICE_CLASSES, + FakeTestDevicePlugin1.class.getCanonicalName()); + nm.init(conf); + nm.start(); + Map pluginMap = rpmSpy.getNameToPlugins(); + Assert.assertEquals(1, pluginMap.size()); + ResourcePlugin rp = pluginMap.get("cmpA.com/hdwA"); + if (!(rp instanceof DevicePluginAdapter)) { + Assert.assertTrue(false); + } + verify(rpmSpy, times(1)).checkInterfaceCompatibility( + DevicePlugin.class, FakeTestDevicePlugin1.class); + } + + // Fail to load a class which doesn't implement interface DevicePlugin + @Test(timeout = 30000) + public void testLoadInvalidPluggableDeviceClasses() + throws Exception{ + ResourcePluginManager rpm = new ResourcePluginManager(); + + ResourcePluginManager rpmSpy = spy(rpm); + nm = new MyMockNM(rpmSpy); + + conf.setBoolean(YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_ENABLED, + true); + conf.setStrings( + YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_DEVICE_CLASSES, + FakeTestDevicePlugin2.class.getCanonicalName()); + + String expectedMessage = "Class: " + + FakeTestDevicePlugin2.class.getCanonicalName() + + " not instance of " + DevicePlugin.class.getCanonicalName(); + String actualMessage = ""; + try { + nm.init(conf); + nm.start(); + } catch (YarnRuntimeException e) { + actualMessage = e.getMessage(); + } + Assert.assertEquals(expectedMessage, actualMessage); + } + + // Fail to register duplicated resource name. + @Test(timeout = 30000) + public void testLoadDuplicateResourceNameDevicePlugin() + throws Exception{ + ResourcePluginManager rpm = new ResourcePluginManager(); + + ResourcePluginManager rpmSpy = spy(rpm); + nm = new MyMockNM(rpmSpy); + + conf.setBoolean(YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_ENABLED, + true); + conf.setStrings( + YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_DEVICE_CLASSES, + FakeTestDevicePlugin1.class.getCanonicalName() + "," + + FakeTestDevicePlugin3.class.getCanonicalName()); + + String expectedMessage = "cmpA.com/hdwA" + + " already registered! Please change resource type name" + + " or configure correct resource type name" + + " in resource-types.xml for " + + FakeTestDevicePlugin3.class.getCanonicalName(); + String actualMessage = ""; + try { + nm.init(conf); + nm.start(); + } catch (YarnRuntimeException e) { + actualMessage = e.getMessage(); + } + Assert.assertEquals(expectedMessage, actualMessage); + } + + /** + * Fail a plugin due to incompatible interface implemented. + * It doesn't implement the "getRegisterRequestInfo" + */ + @Test(timeout = 30000) + public void testIncompatibleDevicePlugin() + throws Exception { + ResourcePluginManager rpm = new ResourcePluginManager(); + + ResourcePluginManager rpmSpy = spy(rpm); + nm = new MyMockNM(rpmSpy); + + conf.setBoolean(YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_ENABLED, + true); + conf.setStrings( + YarnConfiguration.NM_PLUGGABLE_DEVICE_FRAMEWORK_DEVICE_CLASSES, + FakeTestDevicePlugin4.class.getCanonicalName()); + + String expectedMessage = "Method getRegisterRequestInfo" + + " is expected but not implemented in " + + FakeTestDevicePlugin4.class.getCanonicalName(); + String actualMessage = ""; + try { + nm.init(conf); + nm.start(); + } catch (YarnRuntimeException e) { + actualMessage = e.getMessage(); + } + Assert.assertEquals(expectedMessage, actualMessage); + } + + @Test(timeout = 30000) + public void testRequestedResourceNameIsConfigured() + throws Exception{ + ResourcePluginManager rpm = new ResourcePluginManager(); + String resourceName = "a.com/a"; + Assert.assertFalse(rpm.isConfiguredResourceName(resourceName)); + resourceName = "cmp.com/cmp"; + Assert.assertTrue(rpm.isConfiguredResourceName(resourceName)); + } + } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin1.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin1.java new file mode 100644 index 00000000000..d708c8728be --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin1.java @@ -0,0 +1,61 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; + +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.*; + +import java.util.Set; +import java.util.TreeSet; + +/** + * Used only for testing. + * A fake normal vendor plugin + * */ +public class FakeTestDevicePlugin1 implements DevicePlugin { + @Override + public DeviceRegisterRequest getRegisterRequestInfo() { + return DeviceRegisterRequest.Builder.newInstance() + .setResourceName("cmpA.com/hdwA").build(); + } + + @Override + public Set getDevices() { + TreeSet r = new TreeSet<>(); + r.add(Device.Builder.newInstance() + .setId(0) + .setDevPath("/dev/hdwA0") + .setMajorNumber(243) + .setMinorNumber(0) + .setBusID("0000:65:00.0") + .setHealthy(true) + .build()); + return r; + } + + @Override + public DeviceRuntimeSpec onDevicesAllocated(Set allocatedDevices, + YarnRuntimeType yarnRuntime) throws Exception { + return null; + } + + @Override + public void onDevicesReleased(Set allocatedDevices) { + + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin2.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin2.java new file mode 100644 index 00000000000..8c1a61dc21c --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin2.java @@ -0,0 +1,27 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; + +/** + * Only used for testing. + * This isn't a implementation of DevicePlugin + * */ +public class FakeTestDevicePlugin2 { + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin3.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin3.java new file mode 100644 index 00000000000..67bf8dbf3e4 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin3.java @@ -0,0 +1,65 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; + +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.Device; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DevicePlugin; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DeviceRegisterRequest; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DeviceRuntimeSpec; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.YarnRuntimeType; + +import java.util.Set; +import java.util.TreeSet; + +/** + * Only used for testing. + * This plugin register a same name with FakeTestDevicePlugin1 + * */ +public class FakeTestDevicePlugin3 implements DevicePlugin { + @Override + public DeviceRegisterRequest getRegisterRequestInfo() { + return DeviceRegisterRequest.Builder.newInstance() + .setResourceName("cmpA.com/hdwA").build(); + } + + @Override + public Set getDevices() { + TreeSet r = new TreeSet<>(); + r.add(Device.Builder.newInstance() + .setId(0) + .setDevPath("/dev/hdwA0") + .setMajorNumber(243) + .setMinorNumber(0) + .setBusID("0000:65:00.0") + .setHealthy(true) + .build()); + return r; + } + + @Override + public DeviceRuntimeSpec onDevicesAllocated(Set allocatedDevices, + YarnRuntimeType yarnRuntime) throws Exception { + return null; + } + + @Override + public void onDevicesReleased(Set allocatedDevices) { + + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin4.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin4.java new file mode 100644 index 00000000000..445994d635b --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/FakeTestDevicePlugin4.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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; + +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.Device; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DevicePlugin; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DeviceRegisterRequest; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.DeviceRuntimeSpec; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.YarnRuntimeType; + +import java.util.Set; + +/** + * Implement the interface but missed getRegisterRequestInfo method. + * This is equivalent to implements a old version of DevicePlugin + */ +public abstract class FakeTestDevicePlugin4 implements DevicePlugin { + + public DeviceRegisterRequest getOldRegisterRequestInfo() { + return null; + } + + @Override + public Set getDevices() { + return null; + } + + @Override + public DeviceRuntimeSpec onDevicesAllocated(Set allocatedDevices, + YarnRuntimeType yarnRuntime) throws Exception { + return null; + } + + @Override + public void onDevicesReleased(Set releasedDevices) { + + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/TestDevicePluginAdapter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/TestDevicePluginAdapter.java new file mode 100644 index 00000000000..c938b837d4a --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/resourceplugin/deviceframework/TestDevicePluginAdapter.java @@ -0,0 +1,145 @@ +/** + * 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.hadoop.yarn.server.nodemanager.containermanager.resourceplugin.deviceframework; + +import org.apache.hadoop.yarn.api.records.*; +import org.apache.hadoop.yarn.conf.YarnConfiguration; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.server.nodemanager.*; +import org.apache.hadoop.yarn.server.nodemanager.api.deviceplugin.*; +import org.apache.hadoop.yarn.util.resource.ResourceUtils; +import org.apache.hadoop.yarn.util.resource.TestResourceUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.Set; +import java.util.TreeSet; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; + +/** + * Unit tests for DevicePluginAdapter. + * About interaction with vendor plugin + * */ +public class TestDevicePluginAdapter { + + protected static final Logger LOG = + LoggerFactory.getLogger(TestDevicePluginAdapter.class); + + private YarnConfiguration conf; + private String tempResourceTypesFile; + + @Before + public void setup() throws Exception { + this.conf = new YarnConfiguration(); + // setup resource-types.xml + ResourceUtils.resetResourceTypes(); + String resourceTypesFile = "resource-types-pluggable-devices.xml"; + this.tempResourceTypesFile = + TestResourceUtils.setupResourceTypes(this.conf, resourceTypesFile); + } + + @After + public void tearDown() throws IOException { + // cleanup resource-types.xml + File dest = new File(this.tempResourceTypesFile); + if (dest.exists()) { + dest.delete(); + } + } + + @Test + public void testDeviceResourceUpdaterImpl() throws YarnException { + Resource nodeResource = mock(Resource.class); + // Init an plugin + MyPlugin plugin = new MyPlugin(); + MyPlugin spyPlugin = spy(plugin); + String resourceName = MyPlugin.RESOURCE_NAME; + // Init an adapter for the plugin + DevicePluginAdapter adapter = new DevicePluginAdapter( + resourceName, + spyPlugin); + adapter.initialize(mock(Context.class)); + adapter.getNodeResourceHandlerInstance() + .updateConfiguredResource(nodeResource); + verify(spyPlugin, times(1)).getDevices(); + verify(nodeResource, times(1)).setResourceValue( + resourceName, 3); + } + + private class MyPlugin implements DevicePlugin { + private final static String RESOURCE_NAME = "cmpA.com/hdwA"; + @Override + public DeviceRegisterRequest getRegisterRequestInfo() { + return DeviceRegisterRequest.Builder.newInstance() + .setResourceName(RESOURCE_NAME) + .setPluginVersion("v1.0").build(); + } + + @Override + public Set getDevices() { + TreeSet r = new TreeSet<>(); + r.add(Device.Builder.newInstance() + .setId(0) + .setDevPath("/dev/hdwA0") + .setMajorNumber(256) + .setMinorNumber(0) + .setBusID("0000:80:00.0") + .setHealthy(true) + .build()); + r.add(Device.Builder.newInstance() + .setId(1) + .setDevPath("/dev/hdwA1") + .setMajorNumber(256) + .setMinorNumber(0) + .setBusID("0000:80:01.0") + .setHealthy(true) + .build()); + r.add(Device.Builder.newInstance() + .setId(2) + .setDevPath("/dev/hdwA2") + .setMajorNumber(256) + .setMinorNumber(0) + .setBusID("0000:80:02.0") + .setHealthy(true) + .build()); + return r; + } + + @Override + public DeviceRuntimeSpec onDevicesAllocated(Set allocatedDevices, + YarnRuntimeType yarnRuntime) throws Exception { + return null; + } + + @Override + public void onDevicesReleased(Set releasedDevices) { + + } + } // MyPlugin + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/resource-types-pluggable-devices.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/resource-types-pluggable-devices.xml new file mode 100644 index 00000000000..42d3278e3ca --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/resource-types-pluggable-devices.xml @@ -0,0 +1,23 @@ + + + + + + + + yarn.resource-types + cmp.com/cmp,cmpA.com/hdwA + + \ No newline at end of file