diff --git a/build/deploy/production/server/conf/components.xml b/build/deploy/production/server/conf/components.xml
index a40c3456ad3..a7f72cd6931 100755
--- a/build/deploy/production/server/conf/components.xml
+++ b/build/deploy/production/server/conf/components.xml
@@ -95,6 +95,7 @@
+
diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in
index 49901472e1e..13a59d4bc2b 100755
--- a/client/tomcatconf/components.xml.in
+++ b/client/tomcatconf/components.xml.in
@@ -114,7 +114,7 @@
-
+
diff --git a/core/src/com/cloud/agent/AgentManager.java b/core/src/com/cloud/agent/AgentManager.java
index d6c40f541d2..aa86b553307 100755
--- a/core/src/com/cloud/agent/AgentManager.java
+++ b/core/src/com/cloud/agent/AgentManager.java
@@ -29,6 +29,7 @@ import com.cloud.dc.PodCluster;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.DiscoveryException;
import com.cloud.exception.InternalErrorException;
+import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.host.Host;
import com.cloud.host.HostStats;
@@ -158,8 +159,9 @@ public interface AgentManager extends Manager {
* Updates a host
* @param hostId
* @param guestOSCategoryId
+ * @param hostTags
*/
- void updateHost(long hostId, long guestOSCategoryId);
+ void updateHost(long hostId, long guestOSCategoryId, String hostTags) throws UnsupportedOperationException;
/**
* Deletes a host
@@ -209,7 +211,14 @@ public interface AgentManager extends Manager {
public boolean executeUserRequest(long hostId, Event event) throws AgentUnavailableException;
public boolean reconnect(final long hostId) throws AgentUnavailableException;
- public List discoverHosts(long dcId, Long podId, Long clusterId, URI url, String username, String password) throws DiscoveryException;
+ public List discoverHosts(long dcId, Long podId, Long clusterId, URI url, String username, String password, String hostTags) throws DiscoveryException;
Answer easySend(Long hostId, Command cmd, int timeout);
+
+ /**
+ * Returns a comma separated list of tags for the specified host
+ * @param hostId
+ * @return comma separated list of tags
+ */
+ String getHostTags(long hostId);
}
diff --git a/core/src/com/cloud/host/HostTagVO.java b/core/src/com/cloud/host/HostTagVO.java
new file mode 100644
index 00000000000..3b1e898703a
--- /dev/null
+++ b/core/src/com/cloud/host/HostTagVO.java
@@ -0,0 +1,51 @@
+package com.cloud.host;
+
+import javax.persistence.Column;
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.DiscriminatorType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="host_tags")
+public class HostTagVO {
+ @Id
+ @GeneratedValue(strategy=GenerationType.IDENTITY)
+ @Column(name="id")
+ private long id;
+
+ @Column(name="host_id")
+ private long hostId;
+
+ @Column(name="tag")
+ private String tag;
+
+ protected HostTagVO() {
+ }
+
+ public HostTagVO(long hostId, String tag) {
+ this.hostId = hostId;
+ this.tag = tag;
+ }
+
+ public long getHostId() {
+ return hostId;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ public void setTag(String tag) {
+ this.tag = tag;
+ }
+
+ public long getId() {
+ return id;
+ }
+}
diff --git a/core/src/com/cloud/host/HostVO.java b/core/src/com/cloud/host/HostVO.java
index 6aa92ea5752..02fbbb55159 100644
--- a/core/src/com/cloud/host/HostVO.java
+++ b/core/src/com/cloud/host/HostVO.java
@@ -19,7 +19,7 @@ package com.cloud.host;
import java.util.Date;
import java.util.Map;
-
+import java.util.List;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
@@ -129,7 +129,13 @@ public class HostVO implements Host {
// then this field has not been loaded yet.
// Call host dao to load it.
@Transient
- Map details;
+ Map details;
+
+ // This is a delayed load value. If the value is null,
+ // then this field has not been loaded yet.
+ // Call host dao to load it.
+ @Transient
+ List hostTags;
@Override
public String getStorageIpAddressDeux() {
@@ -271,6 +277,14 @@ public class HostVO implements Host {
public void setDetails(Map details) {
this.details = details;
+ }
+
+ public List getHostTags() {
+ return hostTags;
+ }
+
+ public void setHostTags(List hostTags) {
+ this.hostTags = hostTags;
}
@Column(name="data_center_id", nullable=false)
diff --git a/core/src/com/cloud/host/dao/DetailsDao.java b/core/src/com/cloud/host/dao/DetailsDao.java
index e398f353887..ba7a87f7f4a 100644
--- a/core/src/com/cloud/host/dao/DetailsDao.java
+++ b/core/src/com/cloud/host/dao/DetailsDao.java
@@ -17,6 +17,7 @@
*/
package com.cloud.host.dao;
+import java.util.List;
import java.util.Map;
import com.cloud.host.DetailVO;
@@ -30,4 +31,6 @@ public interface DetailsDao extends GenericDao {
DetailVO findDetail(long hostId, String name);
void deleteDetails(long hostId);
+
+ List findHostDetailsbyValue(long hostId, String value);
}
diff --git a/core/src/com/cloud/host/dao/DetailsDaoImpl.java b/core/src/com/cloud/host/dao/DetailsDaoImpl.java
index b10bd048fc6..86e77a0a8e0 100644
--- a/core/src/com/cloud/host/dao/DetailsDaoImpl.java
+++ b/core/src/com/cloud/host/dao/DetailsDaoImpl.java
@@ -17,6 +17,10 @@
*/
package com.cloud.host.dao;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -28,12 +32,15 @@ import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
+import com.cloud.utils.exception.CloudRuntimeException;
@Local(value=DetailsDao.class)
public class DetailsDaoImpl extends GenericDaoBase implements DetailsDao {
protected final SearchBuilder HostSearch;
protected final SearchBuilder DetailSearch;
+ private final String FindHostDetailsByValue = "SELECT host_details.name FROM host_details WHERE host_id = ? and value = ?";
+
protected DetailsDaoImpl() {
HostSearch = createSearchBuilder();
HostSearch.and("hostId", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ);
@@ -77,6 +84,31 @@ public class DetailsDaoImpl extends GenericDaoBase implements De
delete(result.getId());
}
}
+
+ public List findHostDetailsbyValue(long hostId, String value){
+
+ StringBuilder sql = new StringBuilder(FindHostDetailsByValue);
+
+ Transaction txn = Transaction.currentTxn();
+ PreparedStatement pstmt = null;
+ try {
+ pstmt = txn.prepareAutoCloseStatement(sql.toString());
+ pstmt.setLong(1, hostId);
+ pstmt.setString(2, value);
+
+ ResultSet rs = pstmt.executeQuery();
+ List detailNames = new ArrayList();
+
+ while (rs.next()) {
+ detailNames.add(rs.getString("name"));
+ }
+
+ return detailNames;
+ } catch (SQLException e) {
+ throw new CloudRuntimeException("DB Exception on: " + pstmt.toString(), e);
+ }
+
+ }
@Override
public void persist(long hostId, Map details) {
diff --git a/core/src/com/cloud/host/dao/HostDao.java b/core/src/com/cloud/host/dao/HostDao.java
index 78eeea89bfb..ef775c1dccd 100644
--- a/core/src/com/cloud/host/dao/HostDao.java
+++ b/core/src/com/cloud/host/dao/HostDao.java
@@ -135,7 +135,7 @@ public interface HostDao extends GenericDao {
long getNextSequence(long hostId);
- void loadDetails(HostVO host);
-
-
+ void loadDetails(HostVO host);
+
+ void loadHostTags(HostVO host);
}
diff --git a/core/src/com/cloud/host/dao/HostDaoImpl.java b/core/src/com/cloud/host/dao/HostDaoImpl.java
index 83c99a241d1..a7921394061 100644
--- a/core/src/com/cloud/host/dao/HostDaoImpl.java
+++ b/core/src/com/cloud/host/dao/HostDaoImpl.java
@@ -87,6 +87,8 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao
protected final DetailsDaoImpl _detailsDao = ComponentLocator.inject(DetailsDaoImpl.class);
+ protected final HostTagsDaoImpl _hostTagsDao = ComponentLocator.inject(HostTagsDaoImpl.class);
+
public HostDaoImpl() {
_vmHostDao = ComponentLocator.inject(VmHostDaoImpl.class);
@@ -326,6 +328,12 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao
host.setDetails(details);
}
+ @Override
+ public void loadHostTags(HostVO host){
+ List hostTags = _hostTagsDao.gethostTags(host.getId());
+ host.setHostTags(hostTags);
+ }
+
@Override
public boolean updateStatus(HostVO host, Event event, long msId) {
Status oldStatus = host.getStatus();
@@ -488,6 +496,14 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao
_detailsDao.persist(host.getId(), details);
}
+ protected void saveHostTags(HostVO host) {
+ List hostTags = host.getHostTags();
+ if (hostTags == null || (hostTags != null && hostTags.isEmpty())) {
+ return;
+ }
+ _hostTagsDao.persist(host.getId(), hostTags);
+ }
+
@Override
public boolean configure(String name, Map params) throws ConfigurationException {
if (!super.configure(name, params)) {
@@ -509,6 +525,8 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao
HostVO dbHost = super.persist(host);
saveDetails(host);
loadDetails(dbHost);
+ saveHostTags(host);
+ loadHostTags(dbHost);
txn.commit();
@@ -526,6 +544,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao
}
saveDetails(host);
+ saveHostTags(host);
txn.commit();
diff --git a/core/src/com/cloud/host/dao/HostTagsDao.java b/core/src/com/cloud/host/dao/HostTagsDao.java
new file mode 100644
index 00000000000..d7ce813ee96
--- /dev/null
+++ b/core/src/com/cloud/host/dao/HostTagsDao.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.host.dao;
+
+import java.util.List;
+import com.cloud.host.HostTagVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface HostTagsDao extends GenericDao {
+
+ void persist(long hostId, List hostTags);
+
+ List gethostTags(long hostId);
+
+}
+
diff --git a/core/src/com/cloud/host/dao/HostTagsDaoImpl.java b/core/src/com/cloud/host/dao/HostTagsDaoImpl.java
new file mode 100644
index 00000000000..4d1738c6f31
--- /dev/null
+++ b/core/src/com/cloud/host/dao/HostTagsDaoImpl.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.host.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.ejb.Local;
+import com.cloud.host.HostTagVO;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+
+
+@Local(value=HostTagsDao.class)
+public class HostTagsDaoImpl extends GenericDaoBase implements HostTagsDao {
+ protected final SearchBuilder HostSearch;
+
+ protected HostTagsDaoImpl() {
+ HostSearch = createSearchBuilder();
+ HostSearch.and("hostId", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ);
+ HostSearch.done();
+ }
+
+ @Override
+ public List gethostTags(long hostId) {
+ SearchCriteria sc = HostSearch.create();
+ sc.setParameters("hostId", hostId);
+
+ List results = search(sc, null);
+ List hostTags = new ArrayList(results.size());
+ for (HostTagVO result : results) {
+ hostTags.add(result.getTag());
+ }
+
+ return hostTags;
+ }
+
+ @Override
+ public void persist(long hostId, List hostTags) {
+ Transaction txn = Transaction.currentTxn();
+
+ txn.start();
+ for (String tag : hostTags) {
+ HostTagVO vo = new HostTagVO(hostId, tag);
+ persist(vo);
+ }
+ txn.commit();
+ }
+}
diff --git a/core/src/com/cloud/server/ManagementServer.java b/core/src/com/cloud/server/ManagementServer.java
index 3e12c8c5a19..7202fc1d336 100644
--- a/core/src/com/cloud/server/ManagementServer.java
+++ b/core/src/com/cloud/server/ManagementServer.java
@@ -281,7 +281,7 @@ public interface ManagementServer {
* @return true if hosts were found; false if not.
* @throws IllegalArgumentException
*/
- List extends Host> discoverHosts(long dcId, Long podId, Long clusterId, String url, String username, String password) throws IllegalArgumentException, DiscoveryException;
+ List extends Host> discoverHosts(long dcId, Long podId, Long clusterId, String url, String username, String password, String hostTags) throws IllegalArgumentException, DiscoveryException;
String updateAdminPassword(long userId, String oldPassword, String newPassword);
@@ -826,7 +826,7 @@ public interface ManagementServer {
* @param hostId
* @param guestOSCategoryId
*/
- void updateHost(long hostId, long guestOSCategoryId) throws InvalidParameterValueException;
+ void updateHost(long hostId, long guestOSCategoryId, String hostTags) throws InvalidParameterValueException, UnsupportedOperationException;
/**
* Deletes a host
@@ -2182,4 +2182,12 @@ public interface ManagementServer {
String getHyperType();
String getHashKey();
+
+ /**
+ * Returns a comma separated list of tags for the specified host
+ * @param hostId
+ * @return comma separated list of tags
+ */
+ String getHostTags(long hostId);
+
}
diff --git a/core/src/com/cloud/service/ServiceOffering.java b/core/src/com/cloud/service/ServiceOffering.java
index 8160e1a3a97..c968f097ec6 100755
--- a/core/src/com/cloud/service/ServiceOffering.java
+++ b/core/src/com/cloud/service/ServiceOffering.java
@@ -72,5 +72,10 @@ public interface ServiceOffering {
* @return whether or not the service offering requires local storage
*/
boolean getUseLocalStorage();
+
+ /*
+ * @return tag that should be present on the host needed ; optional parameter
+ */
+ public String getHostTag();
}
diff --git a/core/src/com/cloud/service/ServiceOfferingVO.java b/core/src/com/cloud/service/ServiceOfferingVO.java
index 6dc20a91c22..49bc993c99b 100644
--- a/core/src/com/cloud/service/ServiceOfferingVO.java
+++ b/core/src/com/cloud/service/ServiceOfferingVO.java
@@ -54,7 +54,10 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering
@Column(name="guest_ip_type")
@Enumerated(EnumType.STRING)
- private GuestIpType guestIpType;
+ private GuestIpType guestIpType;
+
+ @Column(name="host_tag")
+ private String hostTag;
protected ServiceOfferingVO() {
super();
@@ -140,5 +143,9 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering
public GuestIpType getGuestIpType() {
return guestIpType;
+ }
+
+ public String getHostTag() {
+ return hostTag;
}
}
diff --git a/core/src/com/cloud/storage/dao/StoragePoolDao.java b/core/src/com/cloud/storage/dao/StoragePoolDao.java
index 9bbdd571927..b5b8de1b957 100644
--- a/core/src/com/cloud/storage/dao/StoragePoolDao.java
+++ b/core/src/com/cloud/storage/dao/StoragePoolDao.java
@@ -77,6 +77,8 @@ public interface StoragePoolDao extends GenericDao {
List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags, Boolean shared);
+ List findPoolsByHostTags(long dcId, long podId, Long clusterId, String[] hostTags, Boolean shared);
+
/**
* Find pool by UUID.
*
diff --git a/core/src/com/cloud/storage/dao/StoragePoolDaoImpl.java b/core/src/com/cloud/storage/dao/StoragePoolDaoImpl.java
index e1d39bc1b53..7f0adfcf35e 100644
--- a/core/src/com/cloud/storage/dao/StoragePoolDaoImpl.java
+++ b/core/src/com/cloud/storage/dao/StoragePoolDaoImpl.java
@@ -303,6 +303,30 @@ public class StoragePoolDaoImpl extends GenericDaoBase imp
}
}
+ @Override
+ public List findPoolsByHostTags(long dcId, long podId, Long clusterId, String[] tags, Boolean shared) {
+ List storagePools = null;
+ if (tags == null || tags.length == 0) {
+ storagePools = listBy(dcId, podId, clusterId);
+ } else {
+ Map details = tagsToDetails(tags);
+ storagePools = findPoolsByDetails(dcId, podId, clusterId, details);
+ }
+
+ if (shared == null) {
+ return storagePools;
+ } else {
+ List filteredStoragePools = new ArrayList(storagePools);
+ for (StoragePoolVO pool : storagePools) {
+ if (shared != pool.isShared()) {
+ filteredStoragePools.remove(pool);
+ }
+ }
+
+ return filteredStoragePools;
+ }
+ }
+
@Override
@DB
public List searchForStoragePoolDetails(long poolId, String value){
diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java
index 726269df4c2..4eaae1cb8c4 100755
--- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java
+++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java
@@ -73,6 +73,7 @@ import com.cloud.alert.AlertManager;
import com.cloud.capacity.CapacityVO;
import com.cloud.capacity.dao.CapacityDao;
import com.cloud.configuration.Config;
+import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterIpAddressVO;
@@ -88,6 +89,7 @@ import com.cloud.event.dao.EventDao;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.DiscoveryException;
import com.cloud.exception.InternalErrorException;
+import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.UnsupportedVersionException;
import com.cloud.ha.HighAvailabilityManager;
@@ -145,6 +147,7 @@ import com.cloud.vm.UserVm;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VmCharacteristics;
import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.host.dao.HostTagsDao;
/**
* Implementation of the Agent Manager. This class controls the connection to
@@ -197,6 +200,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
@Inject protected GuestOSCategoryDao _guestOSCategoryDao = null;
@Inject protected DetailsDao _hostDetailsDao = null;
@Inject protected ClusterDao _clusterDao;
+ @Inject protected HostTagsDao _hostTagsDao = null;
private String _publicNic;
private String _privateNic;
@@ -219,6 +223,9 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
@Inject
protected UpgradeManager _upgradeMgr = null;
+ @Inject
+ protected ConfigurationManager _configMgr;
+
protected int _retry = 2;
protected String _name;
@@ -465,11 +472,11 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
- protected AgentAttache handleDirectConnect(ServerResource resource, StartupCommand[] startup, Map details, boolean old) {
+ protected AgentAttache handleDirectConnect(ServerResource resource, StartupCommand[] startup, Map details, boolean old, List hostTags) {
if (startup == null) {
return null;
}
- HostVO server = createHost(startup, resource, details, old);
+ HostVO server = createHost(startup, resource, details, old, hostTags);
if (server == null) {
return null;
}
@@ -482,9 +489,12 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
}
@Override
- public List discoverHosts(long dcId, Long podId, Long clusterId, URI url, String username, String password) throws IllegalArgumentException, DiscoveryException {
+ public List discoverHosts(long dcId, Long podId, Long clusterId, URI url, String username, String password, String hostTags) throws IllegalArgumentException, DiscoveryException {
List hosts = new ArrayList();
s_logger.info("Trying to add a new host at " + url + " in data center " + dcId);
+
+ List hostTagList = _configMgr.csvTagsToList(hostTags);
+
Enumeration en = _discoverers.enumeration();
while (en.hasMoreElements()) {
Discoverer discoverer = en.nextElement();
@@ -493,7 +503,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
for (Map.Entry extends ServerResource, Map> entry : resources.entrySet()) {
ServerResource resource = entry.getKey();
- AgentAttache attache = simulateStart(resource, entry.getValue(), true);
+ AgentAttache attache = simulateStart(resource, entry.getValue(), true, hostTagList);
if (attache != null) {
hosts.add(_hostDao.findById(attache.getId()));
}
@@ -1042,7 +1052,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
_executor.execute(new SimulateStartTask(host.getId(), resource, host.getDetails(), actionDelegate));
}
- protected AgentAttache simulateStart(ServerResource resource, Map details, boolean old) throws IllegalArgumentException{
+ protected AgentAttache simulateStart(ServerResource resource, Map details, boolean old, List hostTags) throws IllegalArgumentException{
StartupCommand[] cmds = resource.initialize();
if (cmds == null )
return null;
@@ -1052,7 +1062,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
s_logger.debug("Startup request from directly connected host: " + new Request(0, -1, -1, cmds, false).toString());
}
try {
- attache = handleDirectConnect(resource, cmds, details, old);
+ attache = handleDirectConnect(resource, cmds, details, old, hostTags);
}catch (IllegalArgumentException ex)
{
s_logger.warn("Unable to connect due to ", ex);
@@ -1155,6 +1165,16 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
}
}
}
+
+ @Override
+ public String getHostTags(long hostId){
+ List hostTags = _hostTagsDao.gethostTags(hostId);
+ if (hostTags == null) {
+ return null;
+ } else {
+ return _configMgr.listToCsvTags(hostTags);
+ }
+ }
@Override
public String getName() {
@@ -1462,7 +1482,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
}
}
- public HostVO createHost(final StartupCommand startup, ServerResource resource, Map details, boolean directFirst) throws IllegalArgumentException {
+ public HostVO createHost(final StartupCommand startup, ServerResource resource, Map details, boolean directFirst, List hostTags) throws IllegalArgumentException {
Host.Type type = null;
if (startup instanceof StartupStorageCommand) {
@@ -1522,6 +1542,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
}
server.setDetails(details);
+ server.setHostTags(hostTags);
updateHost(server, startup, type, _nodeId);
if (resource != null) {
@@ -1570,9 +1591,9 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
return server;
}
- public HostVO createHost(final StartupCommand[] startup, ServerResource resource, Map details, boolean directFirst) throws IllegalArgumentException {
+ public HostVO createHost(final StartupCommand[] startup, ServerResource resource, Map details, boolean directFirst, List hostTags) throws IllegalArgumentException {
StartupCommand firstCmd = startup[0];
- HostVO result = createHost(firstCmd, resource, details, directFirst);
+ HostVO result = createHost(firstCmd, resource, details, directFirst, hostTags);
if( result == null ) {
return null;
}
@@ -1580,7 +1601,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
}
public AgentAttache handleConnect(final Link link, final StartupCommand[] startup) throws IllegalArgumentException {
- HostVO server = createHost(startup, null, null, false);
+ HostVO server = createHost(startup, null, null, false, null);
if ( server == null ) {
return null;
}
@@ -1647,20 +1668,56 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
}
@Override
- public void updateHost(long hostId, long guestOSCategoryId) {
+ public void updateHost(long hostId, long guestOSCategoryId, String hostTags) throws UnsupportedOperationException{
GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId);
Map hostDetails = _hostDetailsDao.findDetails(hostId);
+
+ boolean persistDetails = false;
+ String currentOSCategory = hostDetails.get("guest.os.category.id");
if (guestOSCategory != null) {
- // Save a new entry for guest.os.category.id
- hostDetails.put("guest.os.category.id", String.valueOf(guestOSCategory.getId()));
+ if(!String.valueOf(guestOSCategory.getId()).equals(currentOSCategory)){
+ // Save a new entry for guest.os.category.id
+ hostDetails.put("guest.os.category.id", String.valueOf(guestOSCategory.getId()));
+ persistDetails = true;
+ }
} else {
// Delete any existing entry for guest.os.category.id
- hostDetails.remove("guest.os.category.id");
+ if(currentOSCategory != null){
+ hostDetails.remove("guest.os.category.id");
+ persistDetails = true;
+ }
}
- _hostDetailsDao.persist(hostId, hostDetails);
+ if(persistDetails){
+ _hostDetailsDao.persist(hostId, hostDetails);
+ }
+
+ //update tags
+ List newHostTags = _configMgr.csvTagsToList(hostTags);
+ List oldHostTags = _hostTagsDao.gethostTags(hostId);
+
+ if(areExistingHostTagsRemoved(hostId, newHostTags, oldHostTags)){
+ //throw error - removing the existing host tags is not allowed
+ throw new UnsupportedOperationException("Invalid Operation: Cannot remove existing host tags");
+ }
+ //add the new tags to the host
+ newHostTags.removeAll(oldHostTags);
+ _hostTagsDao.persist(hostId, newHostTags);
}
+
+ private boolean areExistingHostTagsRemoved(long hostId, List newHostTags, List oldHostTags){
+ boolean tagsRemoved = false;
+ if(newHostTags.isEmpty() && !oldHostTags.isEmpty()){
+ tagsRemoved = true;
+ }else if(!newHostTags.isEmpty() && !oldHostTags.isEmpty()){
+ if(!newHostTags.containsAll(oldHostTags)){
+ tagsRemoved = true;
+ }
+ }
+ return tagsRemoved;
+ }
+
protected void updateHost(final HostVO host, final StartupCommand startup, final Host.Type type, final long msId) throws IllegalArgumentException {
s_logger.debug("updateHost() called");
@@ -1890,7 +1947,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Simulating start for resource " + resource.getName() + " id " + id);
}
- simulateStart(resource, details, false);
+ simulateStart(resource, details, false, null);
} catch (Exception e) {
s_logger.warn("Unable to simulate start on resource " + id + " name " + resource.getName(), e);
} finally {
diff --git a/server/src/com/cloud/api/BaseCmd.java b/server/src/com/cloud/api/BaseCmd.java
index 10e5ab4f58f..6cec5b10d75 100644
--- a/server/src/com/cloud/api/BaseCmd.java
+++ b/server/src/com/cloud/api/BaseCmd.java
@@ -397,7 +397,8 @@ public abstract class BaseCmd {
CLUSTER_NAME("clustername", BaseCmd.TYPE_STRING, "clustername"),
SCOPE("scope", BaseCmd.TYPE_STRING, "scope"),
SUM_ACROSS_ZONE("sumacrosszone", BaseCmd.TYPE_BOOLEAN, "sumAcrossZone"),
- EXCEPTION("exception", BaseCmd.TYPE_STRING, "excetpion");
+ EXCEPTION("exception", BaseCmd.TYPE_STRING, "excetpion"),
+ HOST_TAGS("hosttags", BaseCmd.TYPE_STRING, "hostTags");
private final String _name;
private final short _dataType;
diff --git a/server/src/com/cloud/api/commands/AddHostCmd.java b/server/src/com/cloud/api/commands/AddHostCmd.java
index 7c154c9279d..2c9723eb4ac 100644
--- a/server/src/com/cloud/api/commands/AddHostCmd.java
+++ b/server/src/com/cloud/api/commands/AddHostCmd.java
@@ -48,7 +48,9 @@ public class AddHostCmd extends BaseCmd {
s_properties.add(new Pair(BaseCmd.Properties.CLUSTER_NAME, Boolean.FALSE));
s_properties.add(new Pair(BaseCmd.Properties.URL, Boolean.TRUE));
s_properties.add(new Pair(BaseCmd.Properties.USERNAME, Boolean.TRUE));
- s_properties.add(new Pair(BaseCmd.Properties.PASSWORD, Boolean.TRUE));
+ s_properties.add(new Pair(BaseCmd.Properties.PASSWORD, Boolean.TRUE));
+ //host_tags
+ s_properties.add(new Pair(BaseCmd.Properties.HOST_TAGS, Boolean.FALSE));
}
@Override
@@ -70,6 +72,8 @@ public class AddHostCmd extends BaseCmd {
String password = (String)params.get(BaseCmd.Properties.PASSWORD.getName());
Long clusterId = (Long)params.get(BaseCmd.Properties.CLUSTER_ID.getName());
String clusterName = (String)params.get(BaseCmd.Properties.CLUSTER_NAME.getName());
+ //host_tags
+ String hostTags = (String)params.get(BaseCmd.Properties.HOST_TAGS.getName());
if (clusterName != null && clusterId != null) {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Can't specify cluster by both id and name");
@@ -120,7 +124,7 @@ public class AddHostCmd extends BaseCmd {
clusterId = cluster.getId();
}
- List extends Host> h = getManagementServer().discoverHosts(zoneId, podId, clusterId, url, username, password);
+ List extends Host> h = getManagementServer().discoverHosts(zoneId, podId, clusterId, url, username, password, hostTags);
success = !h.isEmpty();
if(success)
@@ -203,6 +207,9 @@ public class AddHostCmd extends BaseCmd {
// calculate memory utilized, we don't provide memory over commit
serverData.add(new Pair(BaseCmd.Properties.MEMORY_USED.getName(), mem));
+
+ //host_tags
+ serverData.add(new Pair(BaseCmd.Properties.HOST_TAGS.getName(), getManagementServer().getHostTags(host.getId())));
}
if (host.getType().toString().equals("Storage")) {
diff --git a/server/src/com/cloud/api/commands/AddSecondaryStorageCmd.java b/server/src/com/cloud/api/commands/AddSecondaryStorageCmd.java
index 8cecb968ca5..5134d43bda7 100644
--- a/server/src/com/cloud/api/commands/AddSecondaryStorageCmd.java
+++ b/server/src/com/cloud/api/commands/AddSecondaryStorageCmd.java
@@ -86,7 +86,7 @@ public class AddSecondaryStorageCmd extends BaseCmd {
List extends Host> h = null;
try {
- h = getManagementServer().discoverHosts(zoneId, null, null, url, null, null);
+ h = getManagementServer().discoverHosts(zoneId, null, null, url, null, null, null);
} catch (Exception ex) {
s_logger.error("Failed to add secondary storage: ", ex);
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Can't add secondary storage with url " + url);
diff --git a/server/src/com/cloud/api/commands/UpdateHostCmd.java b/server/src/com/cloud/api/commands/UpdateHostCmd.java
index 8c5f60cf352..35e4978d21e 100644
--- a/server/src/com/cloud/api/commands/UpdateHostCmd.java
+++ b/server/src/com/cloud/api/commands/UpdateHostCmd.java
@@ -50,6 +50,8 @@ public class UpdateHostCmd extends BaseCmd {
static {
s_properties.add(new Pair(BaseCmd.Properties.ID, Boolean.TRUE));
s_properties.add(new Pair(BaseCmd.Properties.OS_CATEGORY_ID, Boolean.FALSE));
+ //host_tags
+ s_properties.add(new Pair(BaseCmd.Properties.HOST_TAGS, Boolean.FALSE));
}
@Override
@@ -69,6 +71,8 @@ public class UpdateHostCmd extends BaseCmd {
public List> execute(Map params) {
Long id = (Long)params.get(BaseCmd.Properties.ID.getName());
Long guestOSCategoryId = (Long)params.get(BaseCmd.Properties.OS_CATEGORY_ID.getName());
+ //host_tags
+ String hostTags = (String)params.get(BaseCmd.Properties.HOST_TAGS.getName());
if (guestOSCategoryId == null) {
guestOSCategoryId = new Long(-1);
@@ -79,10 +83,15 @@ public class UpdateHostCmd extends BaseCmd {
if (host == null) {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Host with id " + id.toString() + " doesn't exist");
}
+
+
List> returnValues = new ArrayList>();
try {
- getManagementServer().updateHost(id, guestOSCategoryId);
+ getManagementServer().updateHost(id, guestOSCategoryId, hostTags);
+ } catch(UnsupportedOperationException uex){
+ s_logger.error("Failed to update host: ", uex);
+ throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "Failed to update host: " + uex.getMessage());
} catch (Exception ex) {
s_logger.error("Failed to update host: ", ex);
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to update host: " + ex.getMessage());
@@ -176,6 +185,8 @@ public class UpdateHostCmd extends BaseCmd {
// calculate memory utilized, we don't provide memory over commit
hostRO.setMemoryUsed(mem);
+ //host_tags
+ hostRO.setHostTags(getManagementServer().getHostTags(hostVO.getId()));
}
if (hostVO.getType().toString().equals("Storage")) {
hostRO.setDiskSizeTotal(hostVO.getTotalSize());
diff --git a/server/src/com/cloud/async/executor/HostResultObject.java b/server/src/com/cloud/async/executor/HostResultObject.java
index 652d7539c64..5f1761f1cb8 100644
--- a/server/src/com/cloud/async/executor/HostResultObject.java
+++ b/server/src/com/cloud/async/executor/HostResultObject.java
@@ -134,6 +134,9 @@ public class HostResultObject {
@Param(name="networkkbswrite")
private Long networkKbsWrite;
+
+ @Param(name="hostTags")
+ private String hostTags;
public long getId(){
return this.id;
@@ -439,4 +442,12 @@ public class HostResultObject {
public void setNetworkKbsWrite(long networkKbsWrite){
this.networkKbsWrite = networkKbsWrite;
}
+
+ public String getHostTags() {
+ return hostTags;
+ }
+
+ public void setHostTags(String hostTags) {
+ this.hostTags = hostTags;
+ }
}
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index ba1b142be3f..065fb4ed7a0 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -509,7 +509,7 @@ public class ManagementServerImpl implements ManagementServer {
}
@Override
- public List extends Host> discoverHosts(long dcId, Long podId, Long clusterId, String url, String username, String password) throws IllegalArgumentException, DiscoveryException {
+ public List extends Host> discoverHosts(long dcId, Long podId, Long clusterId, String url, String username, String password, String hostTags) throws IllegalArgumentException, DiscoveryException {
URI uri;
try {
uri = new URI(url);
@@ -517,7 +517,7 @@ public class ManagementServerImpl implements ManagementServer {
throw new IllegalArgumentException("Unable to convert the url" + url, e);
}
// TODO: parameter checks.
- return _agentMgr.discoverHosts(dcId, podId, clusterId, uri, username, password);
+ return _agentMgr.discoverHosts(dcId, podId, clusterId, uri, username, password, hostTags);
}
@Override
@@ -536,6 +536,11 @@ public class ManagementServerImpl implements ManagementServer {
}
}
+ @Override
+ public String getHostTags(long hostId) {
+ return _agentMgr.getHostTags(hostId);
+ }
+
@Override
public PreallocatedLunVO registerPreallocatedLun(String targetIqn, String portal, int lun, long size, long dcId, String t) {
String[] tags = null;
@@ -2886,17 +2891,17 @@ public class ManagementServerImpl implements ManagementServer {
return _hostDao.findById(hostId);
}
- public void updateHost(long hostId, long guestOSCategoryId) throws InvalidParameterValueException {
+ public void updateHost(long hostId, long guestOSCategoryId, String hostTags) throws InvalidParameterValueException, UnsupportedOperationException {
// Verify that the guest OS Category exists
if (guestOSCategoryId > 0) {
if (_guestOSCategoryDao.findById(guestOSCategoryId) == null) {
throw new InvalidParameterValueException("Please specify a valid guest OS category.");
}
- }
+ }
- _agentMgr.updateHost(hostId, guestOSCategoryId);
+ _agentMgr.updateHost(hostId, guestOSCategoryId, hostTags);
}
-
+
public boolean deleteHost(long hostId) {
return _agentMgr.deleteHost(hostId);
}
diff --git a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java
index 08ce2417542..a2debb71758 100644
--- a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java
+++ b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java
@@ -52,7 +52,7 @@ public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator {
return null;
}
- List pools = _storagePoolDao.findPoolsByTags(dc.getId(), pod.getId(), clusterId, dskCh.getTags(), null);
+ List pools = findPools(dskCh, offering, dc, pod, clusterId);
if (pools.size() == 0) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("No storage pools available for pod id : " + pod.getId());
@@ -80,4 +80,10 @@ public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator {
return null;
}
}
+
+ protected List findPools(DiskCharacteristicsTO dskCh, ServiceOffering offering, DataCenterVO dc, HostPodVO pod, Long clusterId){
+ List pools = _storagePoolDao.findPoolsByTags(dc.getId(), pod.getId(), clusterId, dskCh.getTags(), null);
+ return pools;
+
+ }
}
diff --git a/server/src/com/cloud/storage/allocator/HostTagBasedLocalStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/HostTagBasedLocalStoragePoolAllocator.java
new file mode 100644
index 00000000000..30a162b2d43
--- /dev/null
+++ b/server/src/com/cloud/storage/allocator/HostTagBasedLocalStoragePoolAllocator.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.storage.allocator;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.manager.allocator.HostAllocator;
+import com.cloud.agent.manager.allocator.impl.FirstFitAllocator;
+import com.cloud.agent.api.to.DiskCharacteristicsTO;
+import com.cloud.capacity.CapacityVO;
+import com.cloud.capacity.dao.CapacityDao;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.HostPodVO;
+import com.cloud.host.Host;
+import com.cloud.service.ServiceOffering;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.ServiceOffering.GuestIpType;
+import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.StoragePoolHostVO;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.StoragePoolHostDao;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.component.Inject;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.SearchCriteria.Func;
+import com.cloud.vm.State;
+import com.cloud.vm.UserVmVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VmCharacteristics;
+import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.storage.StoragePoolVO;
+
+//
+// TODO
+// Rush to make LocalStoragePoolAllocator use static allocation status, we should revisit the overall
+// allocation process to make it more reliable in next release. The code put in here is pretty ugly
+//
+@Local(value=StoragePoolAllocator.class)
+public class HostTagBasedLocalStoragePoolAllocator extends LocalStoragePoolAllocator {
+ private static final Logger s_logger = Logger.getLogger(HostTagBasedLocalStoragePoolAllocator.class);
+
+ public HostTagBasedLocalStoragePoolAllocator() {
+ }
+
+ @Override
+ protected List findPools(DiskCharacteristicsTO dskCh, ServiceOffering offering, DataCenterVO dc, HostPodVO pod, Long clusterId){
+ List pools = _storagePoolDao.findPoolsByHostTags(dc.getId(), pod.getId(), clusterId, dskCh.getTags(), null);
+ return pools;
+ }
+}
diff --git a/setup/db/create-index-fk.sql b/setup/db/create-index-fk.sql
index 44a8f8bee8a..f715491bc3b 100644
--- a/setup/db/create-index-fk.sql
+++ b/setup/db/create-index-fk.sql
@@ -46,6 +46,7 @@ ALTER TABLE `cloud`.`host` ADD INDEX `i_host__pod_id`(`pod_id`);
ALTER TABLE `cloud`.`host` ADD CONSTRAINT `fk_host__cluster_id` FOREIGN KEY `fk_host__cluster_id`(`cluster_id`) REFERENCES `cloud`.`cluster`(`id`);
ALTER TABLE `cloud`.`host_details` ADD CONSTRAINT `fk_host_details__host_id` FOREIGN KEY `fk_host_details__host_id`(`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE;
+ALTER TABLE `cloud`.`host_tags` ADD CONSTRAINT `fk_host_tags__host_id` FOREIGN KEY `fk_host_tags__host_id`(`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE;
ALTER TABLE `cloud`.`storage_pool` ADD CONSTRAINT `fk_storage_pool__pod_id` FOREIGN KEY `fk_storage_pool__pod_id` (`pod_id`) REFERENCES `host_pod_ref` (`id`) ON DELETE CASCADE;
ALTER TABLE `cloud`.`storage_pool` ADD INDEX `i_storage_pool__pod_id`(`pod_id`);
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index ca561d527ef..36c193498c9 100644
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -76,6 +76,7 @@ DROP TABLE IF EXISTS `cloud`.`cluster`;
DROP TABLE IF EXISTS `cloud`.`netapp_volume`;
DROP TABLE IF EXISTS `cloud`.`pool`;
DROP TABLE IF EXISTS `cloud`.`lun`;
+DROP TABLE IF EXISTS `cloud`.`host_tags`;
CREATE TABLE `cloud`.`cluster` (
`id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id',
@@ -391,6 +392,13 @@ CREATE TABLE `cloud`.`op_vm_host` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+CREATE TABLE `cloud`.`host_tags` (
+ `id` bigint unsigned NOT NULL auto_increment,
+ `host_id` bigint unsigned NOT NULL COMMENT 'host id',
+ `tag` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
CREATE TABLE `cloud`.`user` (
`id` bigint unsigned NOT NULL auto_increment,
`username` varchar(255) NOT NULL,
@@ -784,6 +792,7 @@ CREATE TABLE `cloud`.`service_offering` (
`mc_rate` smallint unsigned default 10 COMMENT 'mcast rate throttle mbits/s',
`ha_enabled` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Enable HA',
`guest_ip_type` varchar(255) NOT NULL DEFAULT 'Virtualized' COMMENT 'Type of guest network -- direct or virtualized',
+ `host_tag` varchar(255) COMMENT 'host tag specified by the service_offering',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/setup/db/schema-216to217.sql b/setup/db/schema-216to217.sql
new file mode 100644
index 00000000000..37a93f58b34
--- /dev/null
+++ b/setup/db/schema-216to217.sql
@@ -0,0 +1,10 @@
+CREATE TABLE `cloud`.`host_tags` (
+ `id` bigint unsigned NOT NULL auto_increment,
+ `host_id` bigint unsigned NOT NULL COMMENT 'host id',
+ `tag` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE `cloud`.`host_tags` ADD CONSTRAINT `fk_host_tags__host_id` FOREIGN KEY `fk_host_tags__host_id`(`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE;
+
+ALTER TABLE `cloud`.`service_offering` ADD COLUMN `host_tag` varchar(255);
\ No newline at end of file