Greg Padgett has uploaded a new change for review.

Change subject: restapi: cloud-init [6/6] - rest api for start vm
......................................................................

restapi: cloud-init [6/6] - rest api for start vm

Support using cloud-init to perform initial setup of virtual machines.

Further details available at:
http://www.ovirt.org/Features/Cloud-Init_Integration

This patch adds support for specifying Cloud-Init configuration when
starting a VM using the REST API.

Change-Id: I6ad0bfeca23cf8d4b2887010081d63c258032611
Bug-Url: https://bugzilla.redhat.com/??????
Signed-off-by: Greg Padgett <gpadg...@redhat.com>
---
M 
backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
M 
backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata.yaml
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendCapabilitiesResource.java
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmResource.java
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/VmValidator.java
M 
backend/manager/modules/restapi/types/src/main/java/org/ovirt/engine/api/restapi/types/VmMapper.java
6 files changed, 274 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/37/15537/1

diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
index 5d18fa0..20061ba 100644
--- 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
@@ -583,6 +583,7 @@
           <xs:element ref="nfs_versions" minOccurs="0"/>
           <xs:element ref="pm_proxy_types" minOccurs="0"/>
           <xs:element ref="cpu_modes" minOccurs="0"/>
+          <xs:element ref="attachment_types" minOccurs="0"/>
 
           <!-- Gluster related -->
           <xs:element ref="gluster_volume_types" minOccurs="0"/>
@@ -894,6 +895,20 @@
         <xs:annotation>
           <xs:appinfo>
             <jaxb:property name="CpuModes"/>
+          </xs:appinfo>
+        </xs:annotation>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:element name="attachment_types" type="AttachmentTypes"/>
+
+  <xs:complexType name="AttachmentTypes">
+    <xs:sequence>
+      <xs:element name="attachment_type" type="xs:string" minOccurs="0" 
maxOccurs="unbounded">
+        <xs:annotation>
+          <xs:appinfo>
+            <jaxb:property name="AttachmentTypes"/>
           </xs:appinfo>
         </xs:annotation>
       </xs:element>
@@ -2073,6 +2088,112 @@
     </xs:sequence>
   </xs:complexType>
 
+  <xs:complexType name="CloudInit">
+    <xs:sequence>
+      <xs:element name="hostname" type="xs:string" minOccurs="0"/>
+      <xs:element name="network" minOccurs="0">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="interfaces" minOccurs="0">
+              <xs:complexType>
+                <xs:sequence>
+                  <xs:element name="interface" minOccurs="0" 
maxOccurs="unbounded">
+                    <xs:complexType>
+                      <xs:sequence>
+                        <xs:element name="boot_protocol" type="xs:string"/>
+                        <xs:element name="address" type="xs:string" 
minOccurs="0"/>
+                        <xs:element name="netmask" type="xs:string" 
minOccurs="0"/>
+                        <xs:element name="gateway" type="xs:string" 
minOccurs="0"/>
+                        <xs:element name="onboot" type="xs:boolean"/>
+                      </xs:sequence>
+                      <xs:attribute name="name" type="xs:string" 
use="required"/>
+                    </xs:complexType>
+                  </xs:element>
+                </xs:sequence>
+              </xs:complexType>
+            </xs:element>
+            <xs:element name="dns_servers" minOccurs="0">
+              <xs:complexType>
+                <xs:sequence>
+                  <xs:element name="dns_server" type="xs:string" minOccurs="0" 
maxOccurs="unbounded"/>
+                </xs:sequence>
+              </xs:complexType>
+            </xs:element>
+            <xs:element name="dns_search_domains" minOccurs="0">
+              <xs:complexType>
+                <xs:sequence>
+                  <xs:element name="dns_search_domain" type="xs:string" 
minOccurs="0" maxOccurs="unbounded"/>
+                </xs:sequence>
+              </xs:complexType>
+            </xs:element>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element> <!-- </network> -->
+      <xs:element name="authorized_keys" minOccurs="0">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="authorized_key" minOccurs="0" 
maxOccurs="unbounded">
+              <xs:complexType>
+                <xs:simpleContent>
+                  <xs:extension base="xs:string">
+                    <xs:attribute name="user" type="xs:string" use="required"/>
+                  </xs:extension>
+                </xs:simpleContent>
+              </xs:complexType>
+            </xs:element>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+      <xs:element name="regenerate_ssh_keys" type="xs:boolean" minOccurs="0"/>
+      <xs:element name="timezone" type="xs:string" minOccurs="0"/>
+      <xs:element name="passwords" minOccurs="0">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="password" minOccurs="0" maxOccurs="unbounded">
+              <xs:complexType>
+                <xs:simpleContent>
+                  <xs:extension base="xs:string">
+                    <xs:attribute name="user" type="xs:string" use="required"/>
+                  </xs:extension>
+                </xs:simpleContent>
+              </xs:complexType>
+            </xs:element>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+      <xs:element name="files" minOccurs="0">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="file" minOccurs="0" maxOccurs="unbounded">
+              <xs:complexType>
+                <xs:sequence>
+                  <xs:element name="path" type="xs:string"/>
+                  <xs:element name="content">
+                    <xs:complexType>
+                      <xs:simpleContent>
+                        <xs:extension base="xs:string">
+                          <xs:attribute name="encoding" type="xs:string" 
use="required"/>
+                        </xs:extension>
+                      </xs:simpleContent>
+                    </xs:complexType>
+                  </xs:element>
+                </xs:sequence>
+              </xs:complexType>
+            </xs:element>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:element name="initialization" type="Initialization"/>
+
+  <xs:complexType name="Initialization">
+    <xs:choice>
+      <xs:element name="cloud-init" type="CloudInit" minOccurs="0"/>
+    </xs:choice>
+  </xs:complexType>
+
   <xs:complexType name="VmPlacementPolicy">
     <xs:sequence>
       <xs:element name="host" type="Host" minOccurs="0" maxOccurs="1"/>
@@ -2118,6 +2239,7 @@
           <xs:element ref="domain" minOccurs="0" maxOccurs="1"/>
           <xs:element name="custom_properties" type="CustomProperties" 
minOccurs="0"/>
           <xs:element name="payloads" type="Payloads" minOccurs="0"/>
+          <xs:element name="initialization" type="Initialization" 
minOccurs="0"/>
           <xs:element name="statistics" type="Statistics" minOccurs="0" 
maxOccurs="1"/>
           <xs:element name="disks" type="Disks" minOccurs="0" maxOccurs="1"/>
           <xs:element name="nics" type="Nics" minOccurs="0" maxOccurs="1"/>
diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata.yaml
 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata.yaml
index 43f6d35..def03ee 100644
--- 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata.yaml
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata.yaml
@@ -207,6 +207,24 @@
           action.vm.display.type: 'xs:string', action.vm.stateless: 
'xs:boolean', action.vm.os.cmdline: 'xs:string',
           action.vm.domain.user.username: 'xs:string', action.pause: 
'xs:boolean',
           action.vm.os.boot--COLLECTION: {boot.dev: 'xs:string'}, 
action.vm.domain.user.password: 'xs:string'}
+          action.vm.initialization.cloud-init--COLLECTION: 
{cloud-init.hostname: 'xs:string',
+          cloud-init.network.interfaces.name: 'xs:string',
+          cloud-init.network.interfaces.boot_protocol: 'xs:string',
+          cloud-init.network.interfaces.address: 'xs:string',
+          cloud-init.network.interfaces.netmask: 'xs:string',
+          cloud-init.network.interfaces.gateway: 'xs:string',
+          cloud-init.network.interfaces.onboot: 'xs:boolean',
+          cloud-init.network.dns_servers.dns_server: 'xs:string',
+          cloud-init.network.dns_search_domains.dns_search_domain: 'xs:string',
+          cloud-init.authorized_keys.authorized_key: 'xs:string',
+          cloud-init.authorized_keys.authorized_key.user: 'xs:string',
+          cloud-init.regenerate_ssh_keys: 'xs:boolean',
+          cloud-init.timezone: 'xs:string',
+          cloud-init.passwords.password: 'xs:string',
+          cloud-init.passwords.password.user: 'xs:string',
+          cloud-init.files.file.path: 'xs:string',
+          cloud-init.files.file.content: 'xs:string',
+          cloud-init.files.file.content.encoding: 'xs:string'}
     urlparams: {}
     headers:
       Content-Type: {value: application/xml|json, required: true}
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendCapabilitiesResource.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendCapabilitiesResource.java
index fb050d1..6816744 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendCapabilitiesResource.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendCapabilitiesResource.java
@@ -5,6 +5,8 @@
 import java.util.Set;
 
 import org.ovirt.engine.api.common.util.LinkHelper;
+import org.ovirt.engine.api.model.AttachmentType;
+import org.ovirt.engine.api.model.AttachmentTypes;
 import org.ovirt.engine.api.model.BootDevice;
 import org.ovirt.engine.api.model.BootDevices;
 import org.ovirt.engine.api.model.BootProtocol;
@@ -211,6 +213,7 @@
         addReportedDeviceTypes(version, ReportedDeviceType.values());
         addIpVersions(version, IpVersion.values());
         addCpuModes(version, CpuMode.values());
+        addAttachmentTypes(version, AttachmentType.values());
 
         version.setFeatures(featuresHelper.getFeatures(v));
 
@@ -226,6 +229,15 @@
         return version;
     }
 
+    private void addAttachmentTypes(VersionCaps version, AttachmentType[] 
values) {
+        if (VersionUtils.greaterOrEqual(version, VERSION_3_3)) {
+            version.setAttachmentTypes(new AttachmentTypes());
+            for (AttachmentType mode : values) {
+                
version.getAttachmentTypes().getAttachmentTypes().add(mode.value());
+            }
+        }
+    }
+
     private void addCpuModes(VersionCaps version, CpuMode[] values) {
         if (VersionUtils.greaterOrEqual(version, VERSION_3_2)) {
             version.setCpuModes(new CpuModes());
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmResource.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmResource.java
index e16c67c..d07606c 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmResource.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmResource.java
@@ -244,7 +244,7 @@
                                                                         "VM");
         if (vm.isFirstRun() && vm.getVmOs().isWindows()) {
             params.setInitializationType(InitializationType.Sysprep);
-        } else if (vm.isFirstRun() && vm.getVmOs().isLinux()) {
+        } else if (params.getCloudInitParameters() != null && 
vm.getVmOs().isLinux()) {
             params.setInitializationType(InitializationType.CloudInit);
         } else {
             params.setInitializationType(InitializationType.None);
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/VmValidator.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/VmValidator.java
index 6225034..d5f271b 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/VmValidator.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/VmValidator.java
@@ -14,6 +14,7 @@
     private DisplayValidator displayValidator = new DisplayValidator();
     private PlacementPolicyValidator placementPolicyValidator = new 
PlacementPolicyValidator();
     private PayloadValidator payloadValidator = new PayloadValidator();
+    private CloudInitValidator cloudInitValidator = new CloudInitValidator();
 
     @Override
     public void validateEnums(VM vm) {
@@ -37,5 +38,10 @@
                 payloadValidator.validateEnums(payload);
             }
         }
+        if (vm.isSetInitialization()) {
+            if (vm.getInitialization().isSetCloudInit()) {
+                
cloudInitValidator.validateEnums(vm.getInitialization().getCloudInit());
+            }
+        }
     }
 }
diff --git 
a/backend/manager/modules/restapi/types/src/main/java/org/ovirt/engine/api/restapi/types/VmMapper.java
 
b/backend/manager/modules/restapi/types/src/main/java/org/ovirt/engine/api/restapi/types/VmMapper.java
index 67973d9..6ee6df1 100644
--- 
a/backend/manager/modules/restapi/types/src/main/java/org/ovirt/engine/api/restapi/types/VmMapper.java
+++ 
b/backend/manager/modules/restapi/types/src/main/java/org/ovirt/engine/api/restapi/types/VmMapper.java
@@ -3,16 +3,25 @@
 import static org.ovirt.engine.core.compat.NGuid.createGuidFromString;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
 
 import org.apache.commons.lang.StringUtils;
 import org.ovirt.engine.api.common.util.StatusUtils;
+import org.ovirt.engine.api.model.AttachmentType;
 import org.ovirt.engine.api.model.Boot;
 import org.ovirt.engine.api.model.BootDevice;
 import org.ovirt.engine.api.model.CPU;
+import org.ovirt.engine.api.model.CloudInit;
+import org.ovirt.engine.api.model.CloudInit.AuthorizedKeys.AuthorizedKey;
+import org.ovirt.engine.api.model.CloudInit.Files.File;
+import org.ovirt.engine.api.model.CloudInit.Network.Interfaces.Interface;
+import org.ovirt.engine.api.model.BootProtocol;
+import org.ovirt.engine.api.model.CloudInit.Passwords.Password;
 import org.ovirt.engine.api.model.Cluster;
 import org.ovirt.engine.api.model.CpuMode;
 import org.ovirt.engine.api.model.CpuTopology;
@@ -46,7 +55,9 @@
 import org.ovirt.engine.api.restapi.utils.CustomPropertiesParser;
 import org.ovirt.engine.api.restapi.utils.GuidUtils;
 import org.ovirt.engine.api.restapi.utils.UsbMapperUtils;
+import org.ovirt.engine.core.common.action.CloudInitParameters;
 import org.ovirt.engine.core.common.action.RunVmOnceParams;
+import org.ovirt.engine.core.common.action.CloudInitParameters.Attachment;
 import org.ovirt.engine.core.common.businessentities.BootSequence;
 import org.ovirt.engine.core.common.businessentities.MigrationSupport;
 import org.ovirt.engine.core.common.businessentities.OriginType;
@@ -57,6 +68,8 @@
 import org.ovirt.engine.core.common.businessentities.VmPayload;
 import org.ovirt.engine.core.common.businessentities.VmStatic;
 import org.ovirt.engine.core.common.businessentities.VmTemplate;
+import 
org.ovirt.engine.core.common.businessentities.network.NetworkBootProtocol;
+import 
org.ovirt.engine.core.common.businessentities.network.VdsNetworkInterface;
 import org.ovirt.engine.core.common.utils.VmDeviceType;
 import org.ovirt.engine.core.compat.NGuid;
 import org.ovirt.engine.core.compat.Version;
@@ -485,6 +498,11 @@
                 if (vm.getDomain().getUser().isSetPassword()) {
                     
params.setSysPrepPassword(vm.getDomain().getUser().getPassword());
                 }
+            }
+        }
+        if (vm.isSetInitialization()) {
+            if (vm.getInitialization().isSetCloudInit()) {
+                
params.setCloudInitParameters(map(vm.getInitialization().getCloudInit(), null));
             }
         }
 
@@ -949,6 +967,103 @@
         return entity;
     }
 
+    @Mapping(from = Attachment.AttachmentType.class, to = 
org.ovirt.engine.api.model.AttachmentType.class)
+    public static org.ovirt.engine.api.model.AttachmentType 
map(Attachment.AttachmentType attachmentType, 
org.ovirt.engine.api.model.AttachmentType template) {
+        switch (attachmentType) {
+            case BASE64:            return 
org.ovirt.engine.api.model.AttachmentType.BASE64;
+            case PLAINTEXT:         return 
org.ovirt.engine.api.model.AttachmentType.PLAINTEXT;
+            default:                return null;
+        }
+    }
+
+    @Mapping(from = org.ovirt.engine.api.model.AttachmentType.class, to = 
Attachment.AttachmentType.class)
+    public static Attachment.AttachmentType 
map(org.ovirt.engine.api.model.AttachmentType attachmentType, 
Attachment.AttachmentType template) {
+        switch (attachmentType) {
+            case BASE64:            return Attachment.AttachmentType.BASE64;
+            case PLAINTEXT:         return Attachment.AttachmentType.PLAINTEXT;
+            default:                return null;
+        }
+    }
+
+    @Mapping(from = CloudInit.class, to = CloudInitParameters.class)
+    public static CloudInitParameters map(CloudInit model, CloudInitParameters 
template) {
+        CloudInitParameters entity = template != null ? template : new 
CloudInitParameters();
+
+        entity.setHostname(model.getHostname());
+
+        if (model.getAuthorizedKeys() != null
+                && !model.getAuthorizedKeys().getAuthorizedKey().isEmpty()) {
+            StringBuilder keys = new StringBuilder();
+            for (AuthorizedKey key : 
model.getAuthorizedKeys().getAuthorizedKey()) {
+                if ("root".equals(key.getUser())) {
+                    if (keys.length() > 0) {
+                        keys.append("\n");
+                    }
+                    keys.append(key.getValue());
+                }
+            }
+            entity.setAuthorizedKeys(keys.toString());
+        }
+
+        entity.setRegenerateKeys(model.isRegenerateSshKeys());
+
+        if (model.getNetwork() != null) {
+            if (model.getNetwork().getInterfaces() != null
+                    && 
!model.getNetwork().getInterfaces().getInterface().isEmpty()) {
+                for (Interface iface : 
model.getNetwork().getInterfaces().getInterface()) {
+                    VdsNetworkInterface vdsNetworkInterface = new 
VdsNetworkInterface();
+                    NetworkBootProtocol protocol = 
HostNicMapper.map(BootProtocol.fromValue(iface.getBootProtocol()), null);
+                    vdsNetworkInterface.setBootProtocol(protocol);
+                    if (protocol == NetworkBootProtocol.DHCP) {
+                        vdsNetworkInterface.setAddress(iface.getAddress());
+                        vdsNetworkInterface.setSubnet(iface.getNetmask());
+                        vdsNetworkInterface.setGateway(iface.getGateway());
+                    }
+                    if (iface.isOnboot()) {
+                        if (entity.getStartOnBoot() == null) {
+                            entity.setStartOnBoot(new ArrayList<String>());
+                        }
+                        entity.getStartOnBoot().add(iface.getName());
+                    }
+                }
+
+                if (model.getNetwork().getDnsServers() != null
+                        && 
model.getNetwork().getDnsServers().getDnsServer().isEmpty()) {
+                    
entity.setDnsServers(model.getNetwork().getDnsServers().getDnsServer());
+                }
+
+                if (model.getNetwork().getDnsSearchDomains() != null
+                        && 
model.getNetwork().getDnsSearchDomains().getDnsSearchDomain().isEmpty()) {
+                    
entity.setDnsSearch(model.getNetwork().getDnsSearchDomains().getDnsSearchDomain());
+                }
+            }
+        }
+
+        if (model.getTimezone() != null) {
+            entity.setTimeZone(TimeZone.getTimeZone(model.getTimezone()));
+        }
+
+        if (model.getPasswords() != null && 
!model.getPasswords().getPassword().isEmpty()) {
+            for (Password password : model.getPasswords().getPassword()) {
+                if ("root".equals(password.getUser())) {
+                    entity.setRootPassword(password.getValue());
+                }
+            }
+        }
+
+        if (model.getFiles() != null && !model.getFiles().getFile().isEmpty()) 
{
+            entity.setAttachments(new HashMap<String, Attachment>());
+            for (File file : model.getFiles().getFile()) {
+                Attachment attachment = new Attachment();
+                
attachment.setAttachmentType(map(AttachmentType.fromValue(file.getContent().getEncoding()),
 null));
+                attachment.setContent(file.getContent().getValue());
+                entity.getAttachments().put(file.getPath(), attachment);
+            }
+        }
+
+        return entity;
+    }
+
     static String cpuTuneToString(final CpuTune tune) {
         final StringBuilder builder = new StringBuilder();
         boolean first = true;


-- 
To view, visit http://gerrit.ovirt.org/15537
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6ad0bfeca23cf8d4b2887010081d63c258032611
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Greg Padgett <gpadg...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to