Ovirt虚拟化技术

【Ovirt 笔记】RestAPI 调用机制分析和整理

2018-12-29  本文已影响0人  58bc06151329

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

分析整理的版本为 Ovirt 4.2.3 版本。

1. 概述

RestAPI

方法 作用
GET 查看资源
POST 创建资源
PUT 更新资源
DELETE 移除资源

2. 项目中 RestAPI 的使用

<api>
<link href="/api/capabilities" rel="capabilities"/>
<link href="/api/clusters" rel="clusters"/>
<link href="/api/clusters?search={query}" rel="clusters/search"/>
<link href="/api/datacenters" rel="datacenters"/>
<link href="/api/datacenters?search={query}" rel="datacenters/search"/>
<link href="/api/events" rel="events"/>
<link href="/api/events;from={event_id}?search={query}" rel="events/search"/>
<link href="/api/hosts" rel="hosts"/>
<link href="/api/hosts?search={query}" rel="hosts/search"/>
<link href="/api/networks" rel="networks"/>
<link href="/api/networks?search={query}" rel="networks/search"/>
<link href="/api/roles" rel="roles"/>
<link href="/api/storagedomains" rel="storagedomains"/>
<link href="/api/storagedomains?search={query}" rel="storagedomains/search"/>
<link href="/api/tags" rel="tags"/>
<link href="/api/templates" rel="templates"/>
<link href="/api/templates?search={query}" rel="templates/search"/>
<link href="/api/users" rel="users"/>
<link href="/api/users?search={query}" rel="users/search"/>
<link href="/api/groups" rel="groups"/>
<link href="/api/groups?search={query}" rel="groups/search"/>
<link href="/api/domains" rel="domains"/>
<link href="/api/vmpools" rel="vmpools"/>
<link href="/api/vmpools?search={query}" rel="vmpools/search"/>
<link href="/api/vms" rel="vms"/>
<link href="/api/vms?search={query}" rel="vms/search"/>
<link href="/api/vms/brief" rel="vms/brief"/>
<link href="/api/vms/logoff" rel="vms/logoff"/>
<link href="/api/disks" rel="disks"/>
<link href="/api/disks?search={query}" rel="disks/search"/>
<link href="/api/jobs" rel="jobs"/>
<link href="/api/storageconnections" rel="storageconnections"/>
<link href="/api/vnicprofiles" rel="vnicprofiles"/>
<link href="/api/permissions" rel="permissions"/>
......

查看资源(GET)

例如:

https://<ip>/api/datacenters/<id>

创建资源(POST)

例如:

POST https://<ip>/api/datacenters/

更新资源(PUT)

例如:

PUT https://<ip>/ovirt-engine/api/datacenters/<id>

移除资源(DELETE)

例如:

DELETE https://<ip>/api/datacenters/<id>

3. 项目中实现

模块名称 说明
restapi-defination RestAPI 定义模块
restapi-parent Rest API 后端集成模块(包含 jaxrs、types、webapp、interface、apidoc 等模块)
restapi-jaxrs RestAPI 后端的资源集成模块
restapi-types RestAPI 后端类型(实体)映射模块
restapi-webapp RestAPI 网站(包含 RestAPI 服务器访问路径定义,一些过滤器的设置)
restapi-apidoc RestAPI 文档模块

3.1 restapi-defination 定义

目录 说明
org.ovirt.engine.api.model 数据模型(RestAPI 实体类),初始包含了几个注解、工具类。
org.ovirt.engine.api.resource 资源模块(RestAPI 资源接口),初始包含了几个通用接口。
org.ovirt.engine.api.rsdl RestAPI rsdl 列表模块,包含一些工具类,封装的 rsdl 相关实体类。
org.ovirt.engine.api.utils RestAPI 工具类。
v3 定义了 RestAPI v3 版本的 rsdl.xml 还有 XML schema(api.xsd、api.xjb) 文件。
<!-- Add the model .jar file as a generated resource: -->
<execution>
      <id>copy-model-file</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <artifactItems>
                <artifactItem>
                  <groupId>org.ovirt.engine.api</groupId>
                  <artifactId>model</artifactId>
                  <version>${model.version}</version>
                  <type>jar</type>
                  <classifier>sources</classifier>
                  <outputDirectory>${project.basedir}/target/generated-resources</outputDirectory>
                  <destFileName>model.jar</destFileName>
                </artifactItem>
              </artifactItems>
            </configuration>
</execution>

model.jar

@Type
public interface Vm extends VmBase {
    String stopReason();
    Date startTime();
    Date stopTime();
    ...
}
@Service
public interface VmService extends MeasurableService {
    interface Start {
        @In Boolean pause();
        @In Vm vm();
        @In Boolean useCloudInit();
        @In Boolean useSysprep();
        @In Boolean async();
    }
    ...
}
/**
 * Represents a virtual machine.
 */
@Type
public interface Vm extends VmBase {
    ...
}
/**
 * Represents a virtual machine.
 */
@Type
public interface Vm extends VmBase {
    /**
     * Contains the reason why this virtual machine was stopped. This reason is
     * provided by the user, via the GUI or via the API.
     */
    String stopReason();
    ...
}
/**
 * This service manages a specific virtual machine.
 */
@Service
public interface VmService extends MeasurableService {

    /**
     * This operation will start the virtual machine managed by this
     * service, if it isn't already running.
     */
    interface Start {
        /**
         * Specifies if the virtual machine should be started in pause
         * mode. It is an optional parameter, if not given then the
         * virtual machine will be started normally.
         */
        @In Boolean pause();
        ...
    }
    ...
}
/**
 * Specifies if the virtual machine should be started in pause
 * mode. It is an _optional_ parameter, if not given then the
 * virtual machine will be started normally.
 *
 * To use this parameter with the Python SDK you can use the
 * following code snippet:
 *
 * [source,python]
 * ----
 * # Find the virtual machine:
 * vm = api.vms.get(name="myvm")
 *
 * # Start the virtual machine paused:
 * vm.start(
 *   params.Action(
 *     pause=True
 *   )
 * )
 * ----
 */
@In Boolean pause();

metamodel


<!-- Extract the API documentation contained in the model
               documentation artifact, so that it can then be added
               to the generated .war file: -->
<execution>
            <id>extract-model-documentation</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>unpack</goal>
            </goals>
            <configuration>
              <artifactItems>
                <artifactItem>
                  <groupId>org.ovirt.engine.api</groupId>
                  <artifactId>model</artifactId>
                  <version>${model.version}</version>
                  <type>jar</type>
                  <classifier>javadoc</classifier>
                  <outputDirectory>${project.basedir}/target/generated-resources</outputDirectory>
                </artifactItem>
              </artifactItems>
            </configuration>
</execution>

<!-- Generate the RSDL: -->
<execution>
            <id>generate-rsdl</id>
            <phase>compile</phase>
            <goals>
              <goal>java</goal>
            </goals>
            <configuration>
              <mainClass>org.ovirt.engine.api.rsdl.RsdlManager</mainClass>
              <arguments>
                <argument>${application.baseuri}</argument>
                <argument>${project.build.outputDirectory}/v4/rsdl.xml</argument>
                <argument>${project.build.outputDirectory}/v4/rsdl_gluster.xml</argument>
              </arguments>
            </configuration>
</execution>
private static final String METADATA_FILE_NAME = "/rsdl_metadata.yaml";

private static Rsdl buildRsdl(MetaData metadata, List<String> rels) throws IOException,
            ClassNotFoundException {
        RsdlBuilder builder = new RsdlBuilder(rels, metadata)
        .description(RSDL_DESCRIPTION)
        .rel(RSDL_REL)
                .href(QUERY_PARAMETER + RSDL_CONSTRAINT_PARAMETER)
        .schema(new SchemaBuilder()
            .rel(SCHEMA_REL)
            .href(QUERY_PARAMETER + SCHEMA_CONSTRAINT_PARAMETER)
            .name(SCHEMA_NAME)
            .description(SCHEMA_DESCRIPTION)
            .build())
        .generalMetadata(new GeneralMetadataBuilder()
            .rel(GENERAL_METADATA_REL)
            .href("*")
            .name(GENERAL_METADATA_NAME)
            .description(GENERAL_METADATA_DESCRIPTION)
            .build());
        Rsdl rsdl = builder.build();
        return rsdl;
}

private static MetaData loadMetaData() throws IOException {
        try (InputStream in = RsdlManager.class.getResourceAsStream(METADATA_FILE_NAME)) {
            if (in == null) {
                throw new IOException("Can't find metadata from resource \"" + METADATA_FILE_NAME + "\"");
            }
            return loadMetaData(in);
        }
}

<!-- Parse the model and generate the XML and JSON descriptions, the
               XML schema and the Java code: -->
<execution>
            <id>generate-code</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>exec</goal>
            </goals>
            <configuration>
              <executable>java</executable>
              <classpathScope>runtime</classpathScope>
              <arguments>
                <argument>-classpath</argument>
                <classpath/>
                <argument>org.ovirt.api.metamodel.tool.Main</argument>
                <argument>org.ovirt.api.metamodel.tool.Tool</argument>
                <argument>--model=${project.basedir}/target/generated-resources/model.jar</argument>
                <argument>--in-schema=${project.basedir}/src/main/schema/api.xsd</argument>
                <argument>--out-schema=${project.basedir}/target/generated-resources/v4/api.xsd</argument>
                <argument>--jaxrs=${project.basedir}/target/generated-sources/model</argument>
                <argument>--jaxrs-package=^services\.(.*)$=org.ovirt.engine.api.resource.$1</argument>
                <argument>--jaxrs-package=org.ovirt.engine.api.resource</argument>
              </arguments>
            </configuration>
</execution>
序号 参数 说明
1 --model 为模型解析器提供解析源。
2 --in-schema 指定需要解析的 XML schema 源,/src/main/schema/api.xsd 文件。
3 --out-schema 输出解析后重新生成的 XML schema,自动生成为 /restapi/interface/definition/target/generated-resources/v4/api.xsd 文件。
4 --jaxrs 设置 JAX-RS 源码目录为 /restapi/interface/definition/target/generated-sources/model,加上 5、6 设置自动生成 JAX-RS 源码。
5 --jaxrs-package 设置 JAX-RS 包路径为 /restapi/interface/definition/target/generated-sources/model/org/ovirt/engine/api/resource/$1,其中 $1 通过正则 ^services.(.*)$ 获取 model.jar 中包名称,然后一一对应生成。
6 --jaxrs-package 设置 JAX-RS 包路径为 /restapi/interface/definition/target/generated-sources/model/org/ovirt/engine/api/resource/

--model

private static final String MODEL_OPTION = "model";
File modelFile = (File) line.getParsedOptionValue(MODEL_OPTION);
// Analyze the model files:
Model model = new Model();
ModelAnalyzer modelAnalyzer = new ModelAnalyzer();
modelAnalyzer.setModel(model);
modelAnalyzer.analyzeSource(modelFile);

--in-schema--out-schema

private static final String IN_SCHEMA_OPTION = "in-schema";
private static final String OUT_SCHEMA_OPTION = "out-schema";
File inSchemaFile = (File) line.getParsedOptionValue(IN_SCHEMA_OPTION);
File outSchemaFile = (File) line.getParsedOptionValue(OUT_SCHEMA_OPTION);
// Generate the XML schema:
if (inSchemaFile != null && outSchemaFile != null) {
            schemaGenerator.setInFile(inSchemaFile);
            schemaGenerator.setOutFile(outSchemaFile);
            schemaGenerator.generate(model);
}

--jaxrs--jaxrs-package

private static final String JAXRS_OPTION = "jaxrs";
File jaxrsDir = (File) line.getParsedOptionValue(JAXRS_OPTION);
// Generate the JAX-RS source:
if (jaxrsDir != null) {
            FileUtils.forceMkdir(jaxrsDir);
            jaxrsGenerator.setOutDir(jaxrsDir);
            jaxrsGenerator.generate(model);
            // Generate the JAX-RS helper classes):
            jaxrsHelperGenerator.setOutDir(jaxrsDir);
            jaxrsHelperGenerator.generate(model);
}

<!-- Parse the model and generate replacements for the JAXB enums: -->
<execution>
            <id>generate-enums-jaxb</id>
            <phase>process-sources</phase> <!-- TODO: REVISIT phase -->
            <goals>
              <goal>exec</goal>
            </goals>
            <configuration>
              <executable>java</executable>
              <classpathScope>runtime</classpathScope>
              <arguments>
                <argument>-classpath</argument>
                <classpath/>
                <argument>org.ovirt.api.metamodel.tool.Main</argument>
                <argument>org.ovirt.api.metamodel.tool.EnumGenerationToolJaxb</argument>
                <argument>--model=${project.basedir}/target/generated-resources/model.jar</argument>
                <argument>--xjc=${project.basedir}/target/generated-sources/xjc-v4</argument>
              </arguments>
            </configuration>
</execution>
序号 参数 说明
1 --model 为模型解析器提供解析源。
2 --xjc 最后生成至目录 /restapi/interface/definition/target/generated-sources/xjc-v4/

--xjc

// Generate the enums:
if (xjcDir != null) {
            FileUtils.forceMkdir(xjcDir);
            enumGenerator.setOutDir(xjcDir);
            enumGenerator.generate(model);
}

<!-- Generate the Java code from the XML schema of version 4 of the API: -->
<execution>
            <id>xjc-v4</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <generateDirectory>${project.basedir}/target/generated-sources/xjc-v4</generateDirectory>
              <generatePackage>org.ovirt.engine.api.model</generatePackage>
              <extension>true</extension>
              <schemaDirectory>${project.basedir}/target/generated-resources/v4</schemaDirectory>
              <schemaIncludes>
                <include>api.xsd</include>
              </schemaIncludes>
              <bindingDirectory>${project.basedir}/src/main/schema</bindingDirectory>
              <bindingIncludes>
                <include>api.xjb</include>
              </bindingIncludes>
            </configuration>
</execution>

<!-- Generate the Java code from the XML schema of version 3 of the API: -->
<execution>
            <id>xjc-v3</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <generateDirectory>${project.basedir}/target/generated-sources/xjc-v3</generateDirectory>
              <generatePackage>org.ovirt.engine.api.v3.types</generatePackage>
              <extension>true</extension>
              <schemaDirectory>${project.basedir}/src/main/resources/v3</schemaDirectory>
              <schemaIncludes>
                <include>api.xsd</include>
              </schemaIncludes>
              <bindingDirectory>${project.basedir}/src/main/resources/v3</bindingDirectory>
              <bindingIncludes>
                <include>api.xjb</include>
              </bindingIncludes>
            </configuration>
</execution>

<!-- This is needed to avoid having to manually add the generated sources
           directory to the source paths in Eclipse: -->
<executions>
          <execution>
            <id>add-source</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>add-source</goal>
            </goals>
            <configuration>
              <sources>
                <source>${project.build.directory}/generated-sources/xjc-v3</source>
                <source>${project.build.directory}/generated-sources/xjc-v4</source>
                <source>${project.build.directory}/generated-sources/model</source>
              </sources>
            </configuration>
</execution>
<!-- Include the regular resources directory and also the generated
           resources, including the descriptions of the model and the XML
           schema: -->
<resource>
        <directory>${project.basedir}/src/main/resources</directory>
</resource>
<resource>
        <directory>${project.basedir}/target/generated-resources</directory>
</resource>
目录 来源
org.ovirt.engine.api.model 增加 /restapi/interface/definition/target/generated-sources/xjc-v4/org/ovirt/engine/api/model/ 目录中内容。
org.ovirt.engine.api.resourcel 增加 /restapi/interface/definition/target/generated-sources/model/org/ovirt/engine/api/resource/ 目录中内容。
org.ovirt.engine.api.rsdl 不变
org.ovirt.engine.api.utilsl 不变
org.ovirt.engine.api.v3 /restapi/interface/definition/target/generated-sources/xjc-v3/org/ovirt/engine/api/v3/types/ 目录中内容。
v3 不变
v4 中的 api.xsd 文件 /restapi/interface/definition/target/generated-resources/v4/api.xsd。
v4 资源文件中 rsdl.xml 和 rsdl_gluster.xml 文件 RsdlManager 类直接生成。
API 相关文档 /restapi/interface/definition/target/generated-resources/ 目录下。

3.2 restapi-jaxrs 资源集成

......
@Produces({ ApiMediaType.APPLICATION_XML, ApiMediaType.APPLICATION_JSON })
public interface DataCenterResource  {
    @GET
    default public DataCenter doGet() {
        DataCenter dataCenter = get();
        follow(dataCenter);
        return dataCenter;
    }

    default public DataCenter get() {
        throw new UnsupportedOperationException();
    }
    
    @PUT
    @Consumes({ ApiMediaType.APPLICATION_XML, ApiMediaType.APPLICATION_JSON })
    default public DataCenter update(DataCenter dataCenter) {
        throw new UnsupportedOperationException();
    }
    
    @DELETE
    default public Response remove() {
        throw new UnsupportedOperationException();
    }
    
    default public void follow (ActionableResource entity) {
    }
    
    @Path("storagedomains")
    AttachedStorageDomainsResource getStorageDomainsResource();
    
    @Path("clusters")
    ClustersResource getClustersResource();
    
    @Path("networks")
    DataCenterNetworksResource getNetworksResource();
    
    @Path("permissions")
    AssignedPermissionsResource getPermissionsResource();
    
    @Path("quotas")
    QuotasResource getQuotasResource();
    
    @Path("qoss")
    QossResource getQossResource();
    
    @Path("iscsibonds")
    IscsiBondsResource getIscsiBondsResource();
    
}
......
public class BackendDataCenterResource extends AbstractBackendSubResource<DataCenter, StoragePool>
        implements DataCenterResource {

    public static final String FORCE = "force";

    private final BackendDataCentersResource parent;

    public BackendDataCenterResource(String id, BackendDataCentersResource parent) {
        super(id, DataCenter.class, StoragePool.class);
        this.parent = parent;
    }

    @Override
    public DataCenter get() {
        return performGet(QueryType.GetStoragePoolById, new IdQueryParameters(guid));
    }

    @Override
    public DataCenter update(DataCenter incoming) {
        return performUpdate(incoming,
                new QueryIdResolver<>(QueryType.GetStoragePoolById, IdQueryParameters.class),
                ActionType.UpdateStoragePool,
                new UpdateParametersProvider());
    }

    @Override
    public AssignedPermissionsResource getPermissionsResource() {
        return inject(new BackendAssignedPermissionsResource(guid,
                                                             QueryType.GetPermissionsForObject,
                                                             new GetPermissionsForObjectParameters(guid),
                                                             DataCenter.class,
                                                             VdcObjectType.StoragePool));
    }

    @Override
    public AttachedStorageDomainsResource getStorageDomainsResource() {
        return inject(new BackendAttachedStorageDomainsResource(id));
    }

    @Override
    public DataCenterNetworksResource getNetworksResource() {
        return inject(new BackendDataCenterNetworksResource(id));
    }

    @Override
    public ClustersResource getClustersResource() {
        return inject(new BackendDataCenterClustersResource(id));
    }

    @Override
    public QuotasResource getQuotasResource() {
         return inject(new BackendQuotasResource(id));
    }

    @Override
    public IscsiBondsResource getIscsiBondsResource() {
        return inject(new BackendIscsiBondsResource(id));
    }

    public BackendDataCentersResource getParent() {
        return parent;
    }

    @Override
    protected DataCenter doPopulate(DataCenter model, StoragePool entity) {
        return parent.doPopulate(model, entity);
    }

    @Override
    protected DataCenter deprecatedPopulate(DataCenter model, StoragePool entity) {
        return parent.deprecatedPopulate(model, entity);
    }

    protected class UpdateParametersProvider implements
            ParametersProvider<DataCenter, StoragePool> {
        @Override
        public ActionParametersBase getParameters(DataCenter incoming, StoragePool entity) {
            return new StoragePoolManagementParameter(map(incoming, entity));
        }
    }

    /**
     * Get the storage pool (i.e. datacenter entity) associated with the given
     * cluster.
     */
    @SuppressWarnings("unchecked")
    public static StoragePool getStoragePool(DataCenter dataCenter, AbstractBackendResource parent) {
        StoragePool pool = null;
        if (dataCenter.isSetId()) {
            String id = dataCenter.getId();
            Guid guid;
            try {
                guid = new Guid(id); // can't use asGuid() because the method is static.
            } catch (IllegalArgumentException e) {
                throw new MalformedIdException(e);
            }
            pool = parent.getEntity(StoragePool.class, QueryType.GetStoragePoolById,
                    new IdQueryParameters(guid), "Datacenter: id=" + id);
        } else {
            String clusterName = dataCenter.getName();
            pool = parent.getEntity(StoragePool.class, QueryType.GetStoragePoolByDatacenterName,
                    new NameQueryParameters(clusterName), "Datacenter: name="
                            + clusterName);
            dataCenter.setId(pool.getId().toString());
        }
        return pool;
    }

    /**
     * Get the storage pools (i.e. datacenter entity) associated with the given
     * storagedomain.
     */
    @SuppressWarnings("unchecked")
    public static  List<StoragePool> getStoragePools(Guid storageDomainId, AbstractBackendResource parent) {
        return parent.getEntity(List.class,
                QueryType.GetStoragePoolsByStorageDomainId,
                new IdQueryParameters(storageDomainId),
                "Datacenters",
                true);
    }

    @Override
    public QossResource getQossResource() {
        return inject(new BackendQossResource(id));
    }

    @Override
    public Response remove() {
        get();
        StoragePoolParametersBase params = new StoragePoolParametersBase(asGuid(id));
        boolean force = ParametersHelper.getBooleanParameter(httpHeaders, uriInfo, FORCE, true, false);
        if (force) {
            params.setForceDelete(force);
        }
        return performAction(ActionType.RemoveStoragePool, params);
    }
}

3.3 restapi-types 后端类型(实体)映射

......
public class DataCenterMapper {

    @Mapping(from = DataCenter.class, to = StoragePool.class)
    public static StoragePool map(DataCenter model, StoragePool template) {
        StoragePool entity = template != null ? template : new StoragePool();
        if (model.isSetId()) {
            entity.setId(GuidUtils.asGuid(model.getId()));
        }
        if (model.isSetName()) {
            entity.setName(model.getName());
        }
        if (model.isSetDescription()) {
            entity.setdescription(model.getDescription());
        }
        if (model.isSetComment()) {
            entity.setComment(model.getComment());
        }
        if (model.isSetLocal()) {
            entity.setIsLocal(model.isLocal());
        }
        if (model.isSetStorageFormat()) {
            entity.setStoragePoolFormatType(StorageFormatMapper.map(model.getStorageFormat(), null));
        }
        if (model.isSetVersion() && model.getVersion().getMajor() != null && model.getVersion().getMinor() != null) {
            entity.setCompatibilityVersion(VersionMapper.map(model.getVersion()));
        }

        if (model.isSetMacPool() && model.getMacPool().isSetId()) {
            entity.setMacPoolId(GuidUtils.asGuid(model.getMacPool().getId()));
        }

        if (model.isSetQuotaMode()) {
            entity.setQuotaEnforcementType(map(model.getQuotaMode()));
        }

        return entity;
    }

    @Mapping(from = StoragePool.class, to = DataCenter.class)
    public static DataCenter map(StoragePool entity, DataCenter template) {
        DataCenter model = template != null ? template : new DataCenter();
        model.setId(entity.getId().toString());
        model.setName(entity.getName());
        model.setLocal(entity.isLocal());

        if (!StringUtils.isEmpty(entity.getdescription())) {
                model.setDescription(entity.getdescription());
        }
        if (!StringUtils.isEmpty(entity.getComment())) {
            model.setComment(entity.getComment());
        }
        if (entity.getStatus()!=null) {
            model.setStatus(mapDataCenterStatus(entity.getStatus()));
        }
        if (entity.getCompatibilityVersion() != null) {
            model.setVersion(VersionMapper.map(entity.getCompatibilityVersion()));
        }
        if (entity.getStoragePoolFormatType()!=null) {
            StorageFormat storageFormat = StorageFormatMapper.map(entity.getStoragePoolFormatType(), null);
            if (storageFormat!=null) {
                model.setStorageFormat(storageFormat);
            }
        }

        if (entity.getMacPoolId() != null) {
            model.setMacPool(new MacPool());
            model.getMacPool().setId(entity.getMacPoolId().toString());
        }

        if (entity.getQuotaEnforcementType() != null) {
            model.setQuotaMode(map(entity.getQuotaEnforcementType()));
        }

        return model;
    }

    private static DataCenterStatus mapDataCenterStatus(StoragePoolStatus status) {
        switch (status) {
        case Contend:
            return DataCenterStatus.CONTEND;
        case Maintenance:
            return DataCenterStatus.MAINTENANCE;
        case NotOperational:
            return DataCenterStatus.NOT_OPERATIONAL;
        case NonResponsive:
            return DataCenterStatus.PROBLEMATIC;
        case Uninitialized:
            return DataCenterStatus.UNINITIALIZED;
        case Up:
            return DataCenterStatus.UP;
        default:
            throw new IllegalArgumentException("Unknown data center status \"" + status + "\"");
        }
    }

    @Mapping(from = QuotaEnforcementTypeEnum.class, to = QuotaModeType.class)
    public static QuotaModeType map(QuotaEnforcementTypeEnum type) {
        switch (type) {
        case DISABLED:
            return QuotaModeType.DISABLED;
        case HARD_ENFORCEMENT:
            return QuotaModeType.ENABLED;
        case SOFT_ENFORCEMENT:
            return QuotaModeType.AUDIT;
        default:
            throw new IllegalArgumentException("Unknown quota enforcement type \"" + type + "\"");
        }
    }

    @Mapping(from = QuotaModeType.class, to = QuotaEnforcementTypeEnum.class)
    public static QuotaEnforcementTypeEnum map(QuotaModeType type) {
        switch (type) {
        case DISABLED:
            return QuotaEnforcementTypeEnum.DISABLED;
        case ENABLED:
            return QuotaEnforcementTypeEnum.HARD_ENFORCEMENT;
        case AUDIT:
            return QuotaEnforcementTypeEnum.SOFT_ENFORCEMENT;
        default:
            throw new IllegalArgumentException("Unknown quota mode type \"" + type + "\"");
        }
    }
}

3.4 restapi-apidoc 文档

<jboss-web
  xmlns="http://www.jboss.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_8_0.xsd"
  version="8.0">

  <context-root>/ovirt-engine/apidoc</context-root>
  <symbolic-linking-enabled>true</symbolic-linking-enabled>

</jboss-web>

3.5 restapi-webapp RestAPI 网站

过滤器名称 作用
CORSSupportFilter 跨域支持过滤器,可以通过 ConfigValues.CORSSupport 参数设置是否支持。
CSRFProtectionFilter 伪造请求恶意攻击防护,可以通过 ConfigValues.CSRFProtection 参数设置是否支持。
RestApiSessionValidationFilter RestAPI session 验证。
SessionValidationFilter 系统 session 验证。
SsoRestApiAuthFilter 单点登录 RestAPI 用户授权。
SsoRestApiNegotiationFilter 单点登录 RestAPI 协商验证。
VersionFilter 访问 RestAPI 默认使用版本,ovirt-engine.conf 配置文件中定义。ENGINE_API_DEFAULT_VERSION = 4。使用 API v4 版本。
<servlet>
        <servlet-name>ModelServlet</servlet-name>
        <servlet-class>org.ovirt.api.metamodel.server.ModelServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ModelServlet</servlet-name>
        <url-pattern>/v4/model</url-pattern>
        <url-pattern>/v4/model.xml</url-pattern>
        <url-pattern>/v4/model.json</url-pattern>
        <url-pattern>/v4/model.jar</url-pattern>
        <url-pattern>/v4/model.zip</url-pattern>
        <url-pattern>/v4/model.html</url-pattern>
</servlet-mapping>

3.6 ovirt 4.2.3 新增接口的步骤

  1. 如果要增加实体类,需要在 model 的 types 中定义。
@Type
public interface DataCenter extends Identified {
    Boolean local();
    StorageFormat storageFormat();

    Version version();
    Version[] supportedVersions();
    DataCenterStatus status();
    QuotaModeType quotaMode();

    @Link MacPool macPool();

    @Link StorageDomain[] storageDomains();

    @Link Cluster[] clusters();

    @Link Network[] networks();

    @Link Permission[] permissions();

    @Link Quota[] quotas();

    @Link Qos[] qoss();

    @Link IscsiBond[] iscsiBonds();
}
@Type
public enum DataCenterStatus {
    UNINITIALIZED,
    UP,
    MAINTENANCE,
    NOT_OPERATIONAL,
    PROBLEMATIC,
    CONTEND;
}
  1. 如果要增加资源接口,需要在 model 的 services 中定义。
@Service
@Area("Virtualization")
public interface DataCenterService {

    interface Get extends Follow {
        @Out DataCenter dataCenter();

        @In Boolean filter();
    }

    interface Update {
        @InputDetail
        default void inputDetail() {
            optional(dataCenter().comment());
            optional(dataCenter().description());
            optional(dataCenter().local());
            optional(dataCenter().macPool().id());
            optional(dataCenter().name());
            optional(dataCenter().storageFormat());
            optional(dataCenter().version().major());
            optional(dataCenter().version().minor());
        }
       
        @In @Out DataCenter dataCenter();

        @In Boolean async();
    }

    interface Remove {
        
        @In Boolean force();

        @In Boolean async();
    }

    @Service AttachedStorageDomainsService storageDomains();
    @Service ClustersService clusters();
    @Service DataCenterNetworksService networks();
    @Service AssignedPermissionsService permissions();
    @Service QuotasService quotas();
    @Service QossService qoss();
    @Service IscsiBondsService iscsiBonds();
}
  1. rsdl-metadata.yaml 文件中定义添加的接口信息,如描述,参数等,最终会生成 rsdl.xml 文件。会在输入 https://<ip>/ovirt-engine/api?rsdl 访问显示。
- name: /datacenters/{datacenter:id}|rel=update
  description: update the specified data center in the system
  request:
    body:
      parameterType: DataCenter
      signatures:
      - mandatoryArguments: {}
        optionalArguments:
          datacenter.name: xs:string
          datacenter.description: xs:string
          datacenter.comment: xs:string
          datacenter.local: xs:boolean
          datacenter.version.major: xs:int
          datacenter.version.minor: xs:int
          datacenter.description: xs:string
          datacenter.storage_format: xs:string
          datacenter.mac_pool.id: xs:string
        description: update the specified data center in the system
- name: /datacenters|rel=get
  description: get a list of data centers in the system
  request:
    urlparams:
      search: {context: query, type: 'xs:string', value: 'search query', required: false}
      case_sensitive: {context: matrix, type: 'xs:boolean', value: true|false, required: false}
    headers:
      Filter: {value: true|false, required: false}
- name: /datacenters/{datacenter:id}|rel=get
  description: get the details of the specified data center in the system
  request:
    headers:
      Filter: {value: true|false, required: false}
- name: /datacenters/{datacenter:id}|rel=delete
  description: delete the specified data center in the system
  request:
    urlparams:
      force:
        context: matrix
        type: xs:boolean
        value: true|false
        required: false
- name: /datacenters|rel=add
  description: add a new data center to the system
  request:
    body:
      parameterType: DataCenter
      signatures:
      - mandatoryArguments:
          datacenter.local: xs:boolean
          datacenter.name: xs:string
        optionalArguments:
          datacenter.comment: xs:string
          datacenter.description: xs:string
          datacenter.storage_format: xs:string
          datacenter.version.major: xs:int
          datacenter.version.minor: xs:int
          datacenter.mac_pool.id: xs:string
        description: add a new data center to the system
  1. 如果需要将 RestAPI 实体与服务器后端实体关联,则需要在 restapi-types 模块中增加 Mapper 映射关系。

  2. 如果需要输入 https://<ip>/ovirt-engine/api 显示的 API 列表中显示接口,则需要在 ApiRootLinksCreator 类中进行定义,如果只需要在 https://<ip>/ovirt-engine/api?rsdl 中显示则不需要定义。

public static Collection<DetailedLink> getLinks(String baseUri) {
        Collection<DetailedLink> links = new LinkedList<>();
        links.add(createLink("clusters", LinkFlags.SEARCHABLE, baseUri));
        links.add(createLink("datacenters", LinkFlags.SEARCHABLE, baseUri));
        links.add(createLink("events", LinkFlags.SEARCHABLE, getEventParams(), baseUri));
        links.add(createLink("hosts", LinkFlags.SEARCHABLE, baseUri));
        links.add(createLink("networks", LinkFlags.SEARCHABLE, baseUri));
......
<xs:element name="action" type="Action"/>

  <xs:complexType name="Action">
    <xs:complexContent>
      <xs:extension base="BaseResource">
        <xs:sequence> 
......
          <xs:element maxOccurs="1" minOccurs="0" name="vm" type="Vm"/>
......          
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

4. 与 ovirt 3.4.5 区别

4.1 实现原理方式的区别

4.2 接口的区别

新增接口

接口 方法
affinitylabels add
affinitylabels get
affinitylabels/{affinitylabel:id} delete
affinitylabels/{affinitylabel:id} get
affinitylabels/{affinitylabel:id} update
affinitylabels/{affinitylabel:id}/hosts add
affinitylabels/{affinitylabel:id}/hosts get
affinitylabels/{affinitylabel:id}/hosts/{host:id} delete
affinitylabels/{affinitylabel:id}/hosts/{host:id} get
affinitylabels/{affinitylabel:id}/vms add
affinitylabels/{affinitylabel:id}/vms get
affinitylabels/{affinitylabel:id}/vms/{vm:id} delete
affinitylabels/{affinitylabel:id}/vms/{vm:id} get
clusterlevels get
clusterlevels/{clusterlevel:id} get
clusterlevels/{clusterlevel:id}/clusterfeatures get
clusterlevels/{clusterlevel:id}/clusterfeatures/{clusterfeature:id} get
clusters/{cluster:id}/enabledfeatures add
clusters/{cluster:id}/enabledfeatures get
clusters/{cluster:id}/enabledfeatures/{enabledfeature:id} delete
clusters/{cluster:id}/enabledfeatures/{enabledfeature:id} get
clusters/{cluster:id}/externalnetworkproviders get
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/getprofilestatistics getprofilestatistics
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks add
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks delete
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks get
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/activate activate
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/migrate migrate
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/stopmigrate stopmigrate
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id} delete
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id} get
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id}/replace replace
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id}/statistics get
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id}/statistics/{statistic:id} get
clusters/{cluster:id}/networkfilters get
datacenters/{datacenter:id}/clusters/{cluster:id}/enabledfeatures add
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks add
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks delete
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks get
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/activate activate
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/migrate migrate
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/stopmigrate stopmigrate
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id} delete
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id} get
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id}/replace replace
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id}/statistics statistics
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/glusterbricks/{glusterbrick:id}/statistics/{statistic:id} get
datacenters/{datacenter:id}/clusters/{cluster:id}/networkfilters get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/networklabels add
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/networklabels get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/networklabels/{networklabel:id} delete
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/networklabels/{networklabel:id} get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageserverconnections add
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageserverconnections get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageserverconnections/{storageserverconnection:id} delete
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageserverconnections/{storageserverconnection:id} get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageserverconnections/{storageserverconnection:id} update
datacenters/{datacenter:id}/storagedomains/{storagedomain:id}/disks/{disk:id}/register register
disks/{disk:id}/reduce reduce
externalvmimports add
groups/{group:id}/roles/{role:id} get
hosts/{host:id}/affinitylabels add
hosts/{host:id}/affinitylabels get
hosts/{host:id}/affinitylabels/{affinitylabel:id} delete
hosts/{host:id}/affinitylabels/{affinitylabel:id} get
hosts/{host:id}/externalnetworkproviderconfigurations get
hosts/{host:id}/nics/{nic:id}/networklabels add
hosts/{host:id}/nics/{nic:id}/networklabels get
hosts/{host:id}/nics/{nic:id}/networklabels/{networklabel:id} delete
hosts/{host:id}/nics/{nic:id}/networklabels/{networklabel:id} get
hosts/{host:id}/upgradecheck upgradecheck
imagetransfers add
imagetransfers get
imagetransfers/{imagetransfer:id} get
imagetransfers/{imagetransfer:id} delete
imagetransfers/{imagetransfer:id} update
imagetransfers/{imagetransfer:id}/cancel cancel
imagetransfers/{imagetransfer:id}/extend extend
imagetransfers/{imagetransfer:id}/finalize finalize
imagetransfers/{imagetransfer:id}/pause pause
imagetransfers/{imagetransfer:id}/resume resume
instancetypes/{instancetype:id}/nics/{nic:id} delete
networkfilters get
networkfilters/{networkfilter:id} get
networks/{network:id}/networklabels add
networks/{network:id}/networklabels get
networks/{network:id}/networklabels/{networklabel:id} delete
networks/{network:id}/networklabels/{networklabel:id} get
openstacknetworkproviders/{openstacknetworkprovider:id}/networks/{network:id}/import import
storagedomains/{storagedomain:id}/disks/{disk:id} update
storagedomains/{storagedomain:id}/disks/{disk:id}/reduce reduce
storagedomains/{storagedomain:id}/reduceluns reduceluns
storagedomains/{storagedomain:id}/updateovfstore updateovfstore
storagedomains/{storagedomain:id}/vms/{vm:id}/diskattachments get
templates/{template:id}/diskattachments get
templates/{template:id}/diskattachments/{diskattachment:id} delete
templates/{template:id}/diskattachments/{diskattachment:id} get
templates/{template:id}/nics/{nic:id} delete
users/{user:id}/groups get
users/{user:id}/roles/{role:id} get
vms/{vm:id}/affinitylabels add
vms/{vm:id}/affinitylabels get
vms/{vm:id}/affinitylabels/{affinitylabel:id} delete
vms/{vm:id}/affinitylabels/{affinitylabel:id} get
vms/{vm:id}/diskattachments add
vms/{vm:id}/diskattachments get
vms/{vm:id}/diskattachments/{diskattachment:id} delete
vms/{vm:id}/diskattachments/{diskattachment:id} get
vms/{vm:id}/diskattachments/{diskattachment:id} update
vms/{vm:id}/graphicsconsoles/{graphicsconsole:id}/proxyticket proxyticket
vms/{vm:id}/graphicsconsoles/{graphicsconsole:id}/remoteviewerconnectionfile remoteviewerconnectionfile
vms/{vm:id}/graphicsconsoles/{graphicsconsole:id}/ticket ticket
vms/{vm:id}/nics/{nic:id}/networkfilterparameters add
vms/{vm:id}/previewsnapshot previewsnapshot
vms/{vm:id}/undosnapshot undosnapshot

删除接口

接口 方法
capabilities get
capabilities/{capability:id} get
clusters/{cluster:id}/glustervolumes/{glustervolume:id} get response.type=GlusterVolumeProfileDetails
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks add
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks delete
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks get
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/activate activate
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/migrate migrate
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/stopmigrate stopmigrate
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id} delete
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id} get
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id}/replace replace
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id}/statistics get
clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id}/statistics/{statistic:id} get
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id} get response.type=GlusterVolumeProfileDetails
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks add
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks delete
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks get
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/activate activate
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/migrate migrate
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/stopmigrate stopmigrate
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id} delete
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id} get
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id}/replace replace
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id}/statistics statistics
datacenters/{datacenter:id}/clusters/{cluster:id}/glustervolumes/{glustervolume:id}/bricks/{brick:id}/statistics/{statistic:id} get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/labels add
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/labels get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/labels/{label:id} delete
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/networks/{network:id}/labels/{label:id} get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageconnections add
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageconnections get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageconnections/{storageconnection:id} delete
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageconnections/{storageconnection:id} get
datacenters/{datacenter:id}/iscsibonds/{iscsibond:id}/storageconnections/{storageconnection:id} update
datacenters/{datacenter:id}/networks/{network:id}/labels add
datacenters/{datacenter:id}/networks/{network:id}/labels get
datacenters/{datacenter:id}/networks/{network:id}/labels/{label:id} delete
datacenters/{datacenter:id}/networks/{network:id}/labels/{label:id} get
datacenters/{datacenter:id}/networks/{network:id}/permissions add
datacenters/{datacenter:id}/networks/{network:id}/permissions/{permission:id} delete
datacenters/{datacenter:id}/networks/{network:id}/permissions/{permission:id} get
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles add
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles get
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles/{vnicprofile:id} delete
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles/{vnicprofile:id} get
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles/{vnicprofile:id}/permissions add
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles/{vnicprofile:id}/permissions get
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles/{vnicprofile:id}/permissions/{permission:id} delete
datacenters/{datacenter:id}/networks/{network:id}/vnicprofiles/{vnicprofile:id}/permissions/{permission:id} get
hosts/{host:id}/nics/{nic:id}/attach attach
hosts/{host:id}/nics/{nic:id}/detach detach
hosts/{host:id}/nics/{nic:id}/labels add
hosts/{host:id}/nics/{nic:id}/labels get
hosts/{host:id}/nics/{nic:id}/labels/{label:id} delete
hosts/{host:id}/nics/{nic:id}/labels/{label:id} get
networks/{network:id}/labels add
"networks/{network:id}/labels get
networks/{network:id}/labels/{label:id} delete
networks/{network:id}/labels/{label:id} get
templates/{template:id}/disks get
templates/{template:id}/disks/{disk:id} delete
templates/{template:id}/disks/{disk:id} get
templates/{template:id}/disks/{disk:id}/copy copy
templates/{template:id}/disks/{disk:id}/export export
vms/{vm:id}/commit_snapshot commit_snapshot
vms/{vm:id}/disks add
vms/{vm:id}/disks get
vms/{vm:id}/disks/{disk:id} get
vms/{vm:id}/disks/{disk:id} update
vms/{vm:id}/disks/{disk:id}/activate activate
vms/{vm:id}/disks/{disk:id}/deactivate deactivate
vms/{vm:id}/disks/{disk:id}/export export
vms/{vm:id}/disks/{disk:id}/move move
vms/{vm:id}/disks/{disk:id}/permissions add
vms/{vm:id}/disks/{disk:id}/permissions get
vms/{vm:id}/disks/{disk:id}/permissions/{permission:id} delete
vms/{vm:id}/disks/{disk:id}/permissions/{permission:id} get
vms/{vm:id}/disks/{disk:id}/statistics statistics
vms/{vm:id}/disks/{disk:id}/statistics/{statistic:id} get
vms/{vm:id}/preview_snapshot preview_snapshot
vms/{vm:id}/snapshots/{snapshot:id}/disks/{disk:id} delete
vms/{vm:id}/undo_snapshot undo_snapshot

修改接口

接口 方法 说明
clusters add 参数增加、删除、修改
clusters/{cluster:id} update 参数增加、删除、修改
datacenters add 参数删除
datacenters/{datacenter:id} delete 参数增加、删除
datacenters/{datacenter:id} update 参数删除
datacenters/{datacenter:id}/clusters add 参数增加、删除、修改
datacenters/{datacenter:id}/clusters/{cluster:id} update 参数增加、删除、修改
datacenters/{datacenter:id}/storagedomains/{storagedomain:id}/disks add 参数增加、删除、修改
disks add 参数增加、修改、删除
hosts add 参数增加、修改、删除
hosts/{host:id} delete 参数增加、删除
hosts/{host:id} get 参数删除
hosts/{host:id} update 参数删除
hosts/{host:id}/deactivate deactivate 参数增加
hosts/{host:id}/install install 参数增加
hosts/{host:id}/networkattachments add 参数增加
hosts/{host:id}/networkattachments/{networkattachment:id} update 参数增加
hosts/{host:id}/nics get 参数增加、删除、修改
hosts/{host:id}/nics add 参数增加、删除、修改
hosts/{host:id}/nics/{nic:id} get 参数增加、删除、修改
hosts/{host:id}/nics/{nic:id} update 参数增加、删除、修改
hosts/{host:id}/nics/{nic:id} delete 参数增加、删除、修改
storageconnections/{storageconnection:id} delete 参数增加、删除
storagedomains add 参数增加、删除、修改
storagedomains/{storagedomain:id} delete 参数增加、删除
storagedomains/{storagedomain:id} update 参数增加、删除、修改
storagedomains/{storagedomain:id}/disks add 参数增加、删除、修改
storagedomains/{storagedomain:id}/vms/{vm:id}/import import 参数增加、删除、修改
storagedomains/{storagedomain:id}/vms/{vm:id}/register register 参数增加
templates add 参数增加、删除、修改
templates/{template:id} update 参数增加、删除、修改
templates/{template:id}/nics add 参数删除
templates/{template:id}/nics/{nic:id} get 参数删除
users/{user:id}/tags/{tag:id} get 参数增加、修改
vmpools/{vmpool:id} update 参数增加、修改
vms add 参数增加、删除
vms/{vm:id} delete 参数增加、删除
vms/{vm:id} update 参数增加、删除
vms/{vm:id}/migrate migrate 参数增加、删除
vms/{vm:id}/nics add 参数删除
vms/{vm:id}/nics/{nic:id} update 参数删除
vms/{vm:id}/start start 参数增加、修改
上一篇下一篇

猜你喜欢

热点阅读