This is an automated email from the ASF dual-hosted git repository.

nvazquez pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 664a46a  PR multi tags in compute offering [#4398] (#4399)
664a46a is described below

commit 664a46a525847feeef2500c292c8f9e2a47337de
Author: DK101010 <[email protected]>
AuthorDate: Mon Aug 16 17:08:40 2021 +0200

    PR multi tags in compute offering [#4398] (#4399)
    
    * [#4398] adapt code to handle multi tag string with commas
    
    * [#4398] remove trailing spaces
    
    * [#4398] add multi host tag support for ingest process
    
    * [#4398] add test for multi tag support in offerings
    
    * [#4398]  update multitag support for DeploymentPlanningManagerImpl
    
    encapsulate multi tag check from Ingest Feature, DepolymentPlanningManager 
into
    HostDaoImpl to prevent code duplicates
    
    * [#4398] move logic to HostVO and add tests
    
    * rename test method
    
    * [#4398] Change string method to apaches StringUtils
    
    * [#4398] modify test for multi tag support
    
    * adapt sql for double tags
    
    Co-authored-by: Dirk Klahre <[email protected]>
---
 .../src/main/java/com/cloud/host/HostVO.java       |  15 ++
 .../main/java/com/cloud/host/dao/HostDaoImpl.java  |  82 ++++++--
 .../src/test/java/com/cloud/host/HostVOTest.java   |  61 ++++++
 .../deploy/DeploymentPlanningManagerImpl.java      |   2 +-
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     |  11 +-
 .../cloudstack/vm/UnmanagedVMsManagerImplTest.java |   5 +
 .../component/test_multi_tag_support.py            | 217 +++++++++++++++++++++
 7 files changed, 365 insertions(+), 28 deletions(-)

diff --git a/engine/schema/src/main/java/com/cloud/host/HostVO.java 
b/engine/schema/src/main/java/com/cloud/host/HostVO.java
index 18dcac9..1931318 100644
--- a/engine/schema/src/main/java/com/cloud/host/HostVO.java
+++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java
@@ -40,10 +40,13 @@ import javax.persistence.Transient;
 
 import com.cloud.agent.api.VgpuTypesInfo;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.offering.ServiceOffering;
 import com.cloud.resource.ResourceState;
 import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.db.GenericDao;
+import java.util.Arrays;
+import org.apache.commons.lang.StringUtils;
 
 @Entity
 @Table(name = "host")
@@ -740,6 +743,18 @@ public class HostVO implements Host {
         this.uuid = uuid;
     }
 
+    public boolean checkHostServiceOfferingTags(ServiceOffering 
serviceOffering){
+        if (serviceOffering == null) {
+            return false;
+        }
+        if (StringUtils.isEmpty(serviceOffering.getHostTag())) {
+            return true;
+        }
+
+        List<String> serviceOfferingTags = 
Arrays.asList(serviceOffering.getHostTag().split(","));
+        return this.getHostTags() != null && 
this.getHostTags().containsAll(serviceOfferingTags);
+    }
+
     @Override
     public PartitionType partitionType() {
         return PartitionType.Host;
diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java
index b19f717..58248ad 100644
--- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java
@@ -70,6 +70,7 @@ import com.cloud.utils.db.SearchCriteria.Op;
 import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.utils.db.UpdateBuilder;
 import com.cloud.utils.exception.CloudRuntimeException;
+import java.util.Arrays;
 
 @DB
 @TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", 
valueColumnName = "sequence", allocationSize = 1)
@@ -78,12 +79,19 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, 
Long> implements HostDao
     private static final Logger status_logger = Logger.getLogger(Status.class);
     private static final Logger state_logger = 
Logger.getLogger(ResourceState.class);
 
+    private static final String LIST_HOST_IDS_BY_COMPUTETAGS = "SELECT 
filtered.host_id, COUNT(filtered.tag) AS tag_count "
+                                                             + "FROM (SELECT 
host_id, tag FROM host_tags GROUP BY host_id,tag) AS filtered "
+                                                             + "WHERE tag 
IN(%s) "
+                                                             + "GROUP BY 
host_id "
+                                                             + "HAVING 
tag_count = %s ";
+    private static final String SEPARATOR = ",";
     private static final String LIST_CLUSTERID_FOR_HOST_TAG = "select distinct 
cluster_id from host join host_tags on host.id = host_tags.host_id and 
host_tags.tag = ?";
     private static final String GET_HOSTS_OF_ACTIVE_VMS = "select h.id " +
             "from vm_instance vm " +
             "join host h on (vm.host_id=h.id) " +
             "where vm.service_offering_id= ? and vm.state not in 
(\"Destroyed\", \"Expunging\", \"Error\") group by h.id";
 
+
     protected SearchBuilder<HostVO> TypePodDcStatusSearch;
 
     protected SearchBuilder<HostVO> IdStatusSearch;
@@ -736,11 +744,6 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, 
Long> implements HostDao
 
     @Override
     public List<HostVO> listByHostTag(Host.Type type, Long clusterId, Long 
podId, long dcId, String hostTag) {
-
-        SearchBuilder<HostTagVO> hostTagSearch = 
_hostTagsDao.createSearchBuilder();
-        HostTagVO tagEntity = hostTagSearch.entity();
-        hostTagSearch.and("tag", tagEntity.getTag(), SearchCriteria.Op.EQ);
-
         SearchBuilder<HostVO> hostSearch = createSearchBuilder();
         HostVO entity = hostSearch.entity();
         hostSearch.and("type", entity.getType(), SearchCriteria.Op.EQ);
@@ -749,10 +752,8 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, 
Long> implements HostDao
         hostSearch.and("cluster", entity.getClusterId(), SearchCriteria.Op.EQ);
         hostSearch.and("status", entity.getStatus(), SearchCriteria.Op.EQ);
         hostSearch.and("resourceState", entity.getResourceState(), 
SearchCriteria.Op.EQ);
-        hostSearch.join("hostTagSearch", hostTagSearch, entity.getId(), 
tagEntity.getHostId(), JoinBuilder.JoinType.INNER);
 
         SearchCriteria<HostVO> sc = hostSearch.create();
-        sc.setJoinParameters("hostTagSearch", "tag", hostTag);
         sc.setParameters("type", type.toString());
         if (podId != null) {
             sc.setParameters("pod", podId);
@@ -764,7 +765,13 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, 
Long> implements HostDao
         sc.setParameters("status", Status.Up.toString());
         sc.setParameters("resourceState", ResourceState.Enabled.toString());
 
-        return listBy(sc);
+        List<HostVO> tmpHosts = listBy(sc);
+        List<HostVO> correctHostsByHostTags = new ArrayList();
+        List<Long> hostIdsByComputeOffTags = 
findHostByComputeOfferings(hostTag);
+
+        tmpHosts.forEach((host) -> { 
if(hostIdsByComputeOffTags.contains(host.getId())) 
correctHostsByHostTags.add(host);});
+
+        return correctHostsByHostTags;
     }
 
     @Override
@@ -1179,28 +1186,69 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, 
Long> implements HostDao
     }
 
     @Override
-    public List<Long> listClustersByHostTag(String hostTagOnOffering) {
+    public List<Long> listClustersByHostTag(String computeOfferingTags) {
         TransactionLegacy txn = TransactionLegacy.currentTxn();
+        String sql = this.LIST_CLUSTERID_FOR_HOST_TAG;
         PreparedStatement pstmt = null;
-        List<Long> result = new ArrayList<Long>();
-        StringBuilder sql = new StringBuilder(LIST_CLUSTERID_FOR_HOST_TAG);
-        // during listing the clusters that cross the threshold
-        // we need to check with disabled thresholds of each cluster if not 
defined at cluster consider the global value
+        List<Long> result = new ArrayList();
+        List<String> tags = 
Arrays.asList(computeOfferingTags.split(this.SEPARATOR));
+        String subselect = getHostIdsByComputeTags(tags);
+        sql = String.format(sql, subselect);
+
         try {
-            pstmt = txn.prepareAutoCloseStatement(sql.toString());
-            pstmt.setString(1, hostTagOnOffering);
+            pstmt = txn.prepareStatement(sql);
+
+            for(int i = 0; i < tags.size(); i++){
+                pstmt.setString(i+1, tags.get(i));
+            }
+
             ResultSet rs = pstmt.executeQuery();
             while (rs.next()) {
                 result.add(rs.getLong(1));
             }
+            pstmt.close();
+            if(result.isEmpty()){
+                throw new CloudRuntimeException("No suitable host found for 
follow compute offering tags: " + computeOfferingTags);
+            }
             return result;
         } catch (SQLException e) {
             throw new CloudRuntimeException("DB Exception on: " + sql, e);
-        } catch (Throwable e) {
-            throw new CloudRuntimeException("Caught: " + sql, e);
         }
     }
 
+    private List<Long> findHostByComputeOfferings(String computeOfferingTags){
+        TransactionLegacy txn = TransactionLegacy.currentTxn();
+        PreparedStatement pstmt = null;
+        List<Long> result = new ArrayList();
+        List<String> tags = 
Arrays.asList(computeOfferingTags.split(this.SEPARATOR));
+        String select = getHostIdsByComputeTags(tags);
+        try {
+            pstmt = txn.prepareStatement(select);
+
+            for(int i = 0; i < tags.size(); i++){
+                pstmt.setString(i+1, tags.get(i));
+            }
+
+            ResultSet rs = pstmt.executeQuery();
+            while (rs.next()) {
+                result.add(rs.getLong(1));
+            }
+            pstmt.close();
+            if(result.isEmpty()){
+                throw new CloudRuntimeException("No suitable host found for 
follow compute offering tags: " + computeOfferingTags);
+            }
+            return result;
+        } catch (SQLException e) {
+            throw new CloudRuntimeException("DB Exception on: " + select, e);
+        }
+    }
+
+    private String getHostIdsByComputeTags(List<String> offeringTags){
+        List<String> questionMarks = new ArrayList();
+        offeringTags.forEach((tag) -> { questionMarks.add("?"); });
+        return String.format(this.LIST_HOST_IDS_BY_COMPUTETAGS, 
String.join(",", questionMarks),questionMarks.size());
+    }
+
     @Override
     public List<HostVO> listHostsWithActiveVMs(long offeringId) {
         TransactionLegacy txn = TransactionLegacy.currentTxn();
diff --git a/engine/schema/src/test/java/com/cloud/host/HostVOTest.java 
b/engine/schema/src/test/java/com/cloud/host/HostVOTest.java
new file mode 100755
index 0000000..7b70aba
--- /dev/null
+++ b/engine/schema/src/test/java/com/cloud/host/HostVOTest.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 com.cloud.host;
+
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.storage.Storage.ProvisioningType;
+import com.cloud.vm.VirtualMachine;
+import java.util.Arrays;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.Before;
+
+public class HostVOTest {
+    HostVO host;
+    ServiceOfferingVO offering;
+
+    @Before
+    public void setUp() throws Exception {
+        host = new HostVO();
+        offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0, false, 
"TestSO", ProvisioningType.THIN, true, 
true,"",false,VirtualMachine.Type.User,false);
+    }
+
+    @Test
+    public void testNoSO() {
+        assertFalse(host.checkHostServiceOfferingTags(null));
+    }
+
+    @Test
+    public void testNoTag() {
+        assertTrue(host.checkHostServiceOfferingTags(offering));
+    }
+
+    @Test
+    public void testRightTag() {
+        host.setHostTags(Arrays.asList("tag1","tag2"));
+        offering.setHostTag("tag2,tag1");
+        assertTrue(host.checkHostServiceOfferingTags(offering));
+    }
+
+    @Test
+    public void testWrongTag() {
+        host.setHostTags(Arrays.asList("tag1","tag2"));
+        offering.setHostTag("tag2,tag4");
+        assertFalse(host.checkHostServiceOfferingTags(offering));
+    }
+}
\ No newline at end of file
diff --git 
a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java 
b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index a225015..2ff19d6 100644
--- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -678,7 +678,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, 
Configurable {
         ServiceOffering offering = vmProfile.getServiceOffering();
         if (offering.getHostTag() != null) {
             _hostDao.loadHostTags(host);
-            if (!(host.getHostTags() != null && 
host.getHostTags().contains(offering.getHostTag()))) {
+            if (!host.checkHostServiceOfferingTags(offering)) {
                 s_logger.debug("Service Offering host tag does not match the 
last host of this VM");
                 return false;
             }
diff --git 
a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java 
b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
index 07cf567..6562370 100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@ -379,17 +379,8 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
     }
 
     private boolean hostSupportsServiceOffering(HostVO host, ServiceOffering 
serviceOffering) {
-        if (host == null) {
-            return false;
-        }
-        if (serviceOffering == null) {
-            return false;
-        }
-        if (Strings.isNullOrEmpty(serviceOffering.getHostTag())) {
-            return true;
-        }
         hostDao.loadHostTags(host);
-        return host.getHostTags() != null && 
host.getHostTags().contains(serviceOffering.getHostTag());
+        return host.checkHostServiceOfferingTags(serviceOffering);
     }
 
     private boolean storagePoolSupportsDiskOffering(StoragePool pool, 
DiskOffering diskOffering) {
diff --git 
a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
 
b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
index 32b0e43..a8c64f3 100644
--- 
a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
+++ 
b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
@@ -82,6 +82,7 @@ import com.cloud.exception.PermissionDeniedException;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
+import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor;
 import com.cloud.network.Network;
 import com.cloud.network.NetworkModel;
@@ -180,6 +181,8 @@ public class UnmanagedVMsManagerImplTest {
     private UserVmDao userVmDao;
     @Mock
     private NicDao nicDao;
+    @Mock
+    private HostDao hostDao;
 
     @Mock
     private VMInstanceVO virtualMachine;
@@ -235,6 +238,7 @@ public class UnmanagedVMsManagerImplTest {
         HostVO hostVO = Mockito.mock(HostVO.class);
         when(hostVO.isInMaintenanceStates()).thenReturn(false);
         hosts.add(hostVO);
+        
when(hostVO.checkHostServiceOfferingTags(Mockito.any())).thenReturn(true);
         when(resourceManager.listHostsInClusterByStatus(Mockito.anyLong(), 
Mockito.any(Status.class))).thenReturn(hosts);
         List<VMTemplateStoragePoolVO> templates = new ArrayList<>();
         when(templatePoolDao.listAll()).thenReturn(templates);
@@ -368,6 +372,7 @@ public class UnmanagedVMsManagerImplTest {
         when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance");
         when(importUnmanageInstanceCmd.getAccountName()).thenReturn(null);
         when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null);
+        doNothing().when(hostDao).loadHostTags(null);
         PowerMockito.mockStatic(UsageEventUtils.class);
         unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd);
     }
diff --git a/test/integration/component/test_multi_tag_support.py 
b/test/integration/component/test_multi_tag_support.py
new file mode 100755
index 0000000..313fffb
--- /dev/null
+++ b/test/integration/component/test_multi_tag_support.py
@@ -0,0 +1,217 @@
+# 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.
+from marvin.cloudstackTestCase import cloudstackTestCase
+from marvin.lib.base import *
+from marvin.lib.common import get_zone, get_domain, get_template
+from marvin.lib.utils import cleanup_resources
+
+
+class Services:
+    """Test multi tag support in compute offerings
+    """
+
+    def __init__(self):
+        self.services = {
+            "account": {
+                "email": "[email protected]",
+                "firstname": "Test",
+                "lastname": "User",
+                "username": "test",
+                # Random characters are appended for unique
+                # username
+                "password": "password",
+            },
+            "service_offering_0": {
+                "name": "NoTag",
+                "displaytext": "NoTag",
+                "cpunumber": 1,
+                "cpuspeed": 100,
+                # in MHz
+                "memory": 128,
+                # In MBs
+            },
+            "service_offering_1": {
+                "name": "OneTag",
+                "displaytext": "OneTag",
+                "cpunumber": 1,
+                "cpuspeed": 100,
+                "hosttags": "tag1",
+                # in MHz
+                "memory": 128,
+                # In MBs
+            },
+            "service_offering_2": {
+                "name": "TwoTag",
+                "displaytext": "TwoTag",
+                "cpunumber": 1,
+                "cpuspeed": 100,
+                "hosttags": "tag2,tag1",
+                # in MHz
+                "memory": 128,
+                # In MBs
+            },
+            "service_offering_not_existing_tag": {
+                "name": "NotExistingTag",
+                "displaytext": "NotExistingTag",
+                "cpunumber": 1,
+                "cpuspeed": 100,
+                "hosttags": "tagX",
+                # in MHz
+                "memory": 128,
+                # In MBs
+            },
+            "virtual_machine": {
+                "name": "TestVM",
+                "displayname": "TestVM"
+            },
+            "template": {
+                "displaytext": "Ubuntu",
+                "name": "Ubuntu16 x64",
+                "ostype": 'Ubuntu 16.04 (64-bit)',
+                "templatefilter": 'self',
+            },
+            "network": {
+                "name": "Guest",
+            },
+            "host": {
+                "name": ""
+            }
+        }
+
+
+class TestMultiTagSupport(cloudstackTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.testClient = super(TestMultiTagSupport, cls).getClsTestClient()
+        cls.hypervisor = cls.testClient.getHypervisorInfo()
+        cls.api_client = cls.testClient.getApiClient()
+        cls.services = Services().services
+        cls.zone = get_zone(cls.api_client)
+        cls.domain = get_domain(cls.api_client)
+        cls.network = cls.get_network()
+        cls.host = cls.get_host()
+        cls.host_tags = cls.host["hosttags"]
+
+        cls.template = get_template(
+            cls.api_client,
+            cls.zone.id,
+            cls.services["template"]["ostype"]
+        )
+
+        cls.service_offering_list = []
+        for x in range(0, 3):
+            cls.service_offering_list.append(ServiceOffering.create(
+                cls.api_client,
+                cls.services["service_offering_" + str(x)]
+            ))
+
+        cls.service_offering_ne_tag = ServiceOffering.create(
+            cls.api_client,
+            cls.services["service_offering_not_existing_tag"]
+        )
+
+        cls.account = Account.create(
+            cls.api_client,
+            cls.services["account"],
+            admin=True,
+        )
+
+        cls._cleanup = [
+                           cls.account,
+                           cls.service_offering_ne_tag,
+                       ] + cls.service_offering_list
+
+    @classmethod
+    def tearDownClass(cls):
+        try:
+            # Cleanup resources used
+            print("Cleanup resources used")
+            Host.update(cls.api_client, id=cls.host["id"], 
hosttags=cls.host_tags)
+            cleanup_resources(cls.api_client, cls._cleanup)
+        except Exception as e:
+            raise Exception("Warning: Exception during cleanup : %s" % e)
+        return
+
+    def test_multi_tags(self):
+        for x in range(0, len(self.service_offering_list)):
+
+            if hasattr(self.service_offering_list[x], 'hosttags'):
+                Host.update(self.api_client, id=self.host["id"], 
hosttags=self.service_offering_list[x].hosttags)
+
+            vm = VirtualMachine.create(
+                self.api_client,
+                self.services["virtual_machine"],
+                accountid=self.account.name,
+                domainid=self.account.domainid,
+                zoneid=self.zone.id,
+                serviceofferingid=self.service_offering_list[x].id,
+                templateid=self.template.id,
+                networkids=self.network.id
+            )
+
+            self.assertEqual(
+                isinstance(vm, VirtualMachine),
+                True,
+                "VM %s should be created with service offering %s " %
+                (self.services["virtual_machine"]["name"], 
self.service_offering_list[x].name))
+
+            vm.delete(self.api_client, True)
+
+    def test_no_existing_tag(self):
+
+        with self.assertRaises(Exception):
+            VirtualMachine.create(
+                self.api_client,
+                self.services["virtual_machine"],
+                accountid=self.account.name,
+                domainid=self.account.domainid,
+                zoneid=self.zone.id,
+                serviceofferingid=self.service_offering_ne_tag.id,
+                templateid=self.template.id,
+                networkids=self.network.id
+            )
+            return
+
+    @classmethod
+    def get_network(cls):
+        networks = Network.list(cls.api_client, zoneid=cls.zone.id)
+        if len(networks) == 1:
+            return networks[0]
+
+        if len(networks) > 1:
+            for network in networks:
+                if network.name == cls.services["network"]["name"]:
+                    return network
+            raise ValueError("No suitable network found, check network name in 
service object")
+
+        raise ValueError("No network found for zone with id %s" % cls.zone.id)
+
+    @classmethod
+    def get_host(cls):
+        hosts = Host.list(cls.api_client, zoneid=cls.zone.id)
+
+        if len(hosts) == 1:
+            return hosts[0]
+
+        if len(hosts) > 1:
+            for host in hosts:
+                if host.name == cls.services["host"]["name"]:
+                    return host
+            raise ValueError("No suitable host found, check host name in 
service object")
+
+        raise ValueError("No host found for zone with id %s" % cls.zone.id)

Reply via email to