| // 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.ucs.manager; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.inject.Inject; |
| import javax.naming.ConfigurationException; |
| |
| import org.apache.cloudstack.api.AddUcsManagerCmd; |
| import org.apache.cloudstack.api.AssociateUcsProfileToBladeCmd; |
| import org.apache.cloudstack.api.DeleteUcsManagerCmd; |
| import org.apache.cloudstack.api.ListUcsBladeCmd; |
| import org.apache.cloudstack.api.ListUcsManagerCmd; |
| import org.apache.cloudstack.api.ListUcsProfileCmd; |
| import org.apache.cloudstack.api.response.ListResponse; |
| import org.apache.cloudstack.api.response.UcsBladeResponse; |
| import org.apache.cloudstack.api.response.UcsManagerResponse; |
| import org.apache.cloudstack.api.response.UcsProfileResponse; |
| import org.apache.cloudstack.framework.config.dao.ConfigurationDao; |
| import org.apache.cloudstack.managed.context.ManagedContextRunnable; |
| import org.apache.log4j.Logger; |
| |
| import com.cloud.configuration.Config; |
| import com.cloud.dc.ClusterDetailsDao; |
| import com.cloud.dc.DataCenterVO; |
| import com.cloud.dc.dao.ClusterDao; |
| import com.cloud.dc.dao.DataCenterDao; |
| import com.cloud.host.HostVO; |
| import com.cloud.host.dao.HostDao; |
| import com.cloud.resource.ResourceService; |
| import com.cloud.ucs.database.UcsBladeDao; |
| import com.cloud.ucs.database.UcsBladeVO; |
| import com.cloud.ucs.database.UcsManagerDao; |
| import com.cloud.ucs.database.UcsManagerVO; |
| import com.cloud.ucs.structure.ComputeBlade; |
| import com.cloud.ucs.structure.UcsCookie; |
| import com.cloud.ucs.structure.UcsProfile; |
| import com.cloud.utils.concurrency.NamedThreadFactory; |
| import com.cloud.utils.db.DB; |
| import com.cloud.utils.db.QueryBuilder; |
| import com.cloud.utils.db.SearchCriteria.Op; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.utils.xmlobject.XmlObject; |
| import com.cloud.utils.xmlobject.XmlObjectParser; |
| |
| public class UcsManagerImpl implements UcsManager { |
| public static final Logger s_logger = Logger.getLogger(UcsManagerImpl.class); |
| public static final Long COOKIE_TTL = TimeUnit.MILLISECONDS.convert(100L, TimeUnit.MINUTES); |
| public static final Long COOKIE_REFRESH_TTL = TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES); |
| |
| @Inject |
| private UcsManagerDao ucsDao; |
| @Inject |
| private ResourceService resourceService; |
| @Inject |
| private ClusterDao clusterDao; |
| @Inject |
| private ClusterDetailsDao clusterDetailsDao; |
| @Inject |
| private UcsBladeDao bladeDao; |
| @Inject |
| private HostDao hostDao; |
| @Inject |
| private DataCenterDao dcDao; |
| @Inject |
| private ConfigurationDao configDao; |
| |
| private final Map<Long, UcsCookie> cookies = new HashMap<Long, UcsCookie>(); |
| private String name; |
| private int runLevel; |
| private Map<String, Object> params; |
| private ScheduledExecutorService syncBladesExecutor; |
| private int syncBladeInterval; |
| |
| private class SyncBladesThread extends ManagedContextRunnable { |
| |
| private void discoverNewBlades(Map<String, UcsBladeVO> previous, Map<String, ComputeBlade> now, UcsManagerVO mgr) { |
| for (Map.Entry<String, ComputeBlade> e : now.entrySet()) { |
| String dn = e.getKey(); |
| if (previous.keySet().contains(dn)) { |
| continue; |
| } |
| |
| ComputeBlade nc = e.getValue(); |
| UcsBladeVO vo = new UcsBladeVO(); |
| vo.setDn(nc.getDn()); |
| vo.setUcsManagerId(mgr.getId()); |
| vo.setUuid(UUID.randomUUID().toString()); |
| bladeDao.persist(vo); |
| s_logger.debug(String.format("discovered a new UCS blade[dn:%s] during sync", nc.getDn())); |
| } |
| } |
| |
| private void decommissionFadedBlade(Map<String, UcsBladeVO> previous, Map<String, ComputeBlade> now) { |
| for (Map.Entry<String, UcsBladeVO> e : previous.entrySet()) { |
| String dn = e.getKey(); |
| if (now.keySet().contains(dn)) { |
| continue; |
| } |
| |
| UcsBladeVO vo = e.getValue(); |
| bladeDao.remove(vo.getId()); |
| s_logger.debug(String.format("decommission faded blade[dn:%s] during sync", vo.getDn())); |
| } |
| } |
| |
| private void syncBlades(UcsManagerVO mgr) { |
| QueryBuilder<UcsBladeVO> q = QueryBuilder.create(UcsBladeVO.class); |
| q.and(q.entity().getUcsManagerId(), Op.EQ, mgr.getId()); |
| List<UcsBladeVO> pblades = q.list(); |
| if (pblades.isEmpty()) { |
| return; |
| } |
| |
| Map<String, UcsBladeVO> previousBlades = new HashMap<String, UcsBladeVO>(pblades.size()); |
| for (UcsBladeVO b : pblades) { |
| previousBlades.put(b.getDn(), b); |
| } |
| |
| List<ComputeBlade> cblades = listBlades(mgr.getId()); |
| Map<String, ComputeBlade> currentBlades = new HashMap<String, ComputeBlade>(cblades.size()); |
| for (ComputeBlade c : cblades) { |
| currentBlades.put(c.getDn(), c); |
| } |
| |
| discoverNewBlades(previousBlades, currentBlades, mgr); |
| decommissionFadedBlade(previousBlades, currentBlades); |
| } |
| |
| @Override |
| protected void runInContext() { |
| try { |
| List<UcsManagerVO> mgrs = ucsDao.listAll(); |
| for (UcsManagerVO mgr : mgrs) { |
| syncBlades(mgr); |
| } |
| } catch (Throwable t) { |
| s_logger.warn(t.getMessage(), t); |
| } |
| } |
| |
| } |
| |
| @Override |
| public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { |
| return true; |
| } |
| |
| @Override |
| public boolean start() { |
| try { |
| syncBladeInterval = Integer.parseInt(configDao.getValue(Config.UCSSyncBladeInterval.key())); |
| } catch (NumberFormatException e) { |
| syncBladeInterval = 600; |
| } |
| syncBladesExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("UCS-SyncBlades")); |
| syncBladesExecutor.scheduleAtFixedRate(new SyncBladesThread(), syncBladeInterval, syncBladeInterval, TimeUnit.SECONDS); |
| return true; |
| } |
| |
| @Override |
| public boolean stop() { |
| return true; |
| } |
| |
| @Override |
| public String getName() { |
| return name; |
| } |
| |
| private void discoverBlades(UcsManagerVO ucsMgrVo) { |
| List<ComputeBlade> blades = listBlades(ucsMgrVo.getId()); |
| for (ComputeBlade b : blades) { |
| UcsBladeVO vo = new UcsBladeVO(); |
| vo.setDn(b.getDn()); |
| vo.setUcsManagerId(ucsMgrVo.getId()); |
| vo.setUuid(UUID.randomUUID().toString()); |
| bladeDao.persist(vo); |
| } |
| } |
| |
| @Override |
| @DB |
| public UcsManagerResponse addUcsManager(AddUcsManagerCmd cmd) { |
| QueryBuilder<UcsManagerVO> q = QueryBuilder.create(UcsManagerVO.class); |
| q.and(q.entity().getUrl(), Op.EQ, cmd.getUrl()); |
| UcsManagerVO mgrvo = q.find(); |
| if (mgrvo != null) { |
| throw new IllegalArgumentException(String.format("duplicate UCS manager. url[%s] is used by another UCS manager already", cmd.getUrl())); |
| } |
| |
| try { |
| UcsManagerVO vo = new UcsManagerVO(); |
| vo.setUuid(UUID.randomUUID().toString()); |
| vo.setPassword(cmd.getPassword()); |
| vo.setUrl(cmd.getUrl()); |
| vo.setUsername(cmd.getUsername()); |
| vo.setZoneId(cmd.getZoneId()); |
| vo.setName(cmd.getName()); |
| |
| mgrvo = ucsDao.persist(vo); |
| UcsManagerResponse rsp = new UcsManagerResponse(); |
| rsp.setId(String.valueOf(vo.getId())); |
| rsp.setName(vo.getName()); |
| rsp.setUrl(vo.getUrl()); |
| rsp.setZoneId(String.valueOf(vo.getZoneId())); |
| |
| discoverBlades(vo); |
| return rsp; |
| } catch (CloudRuntimeException e) { |
| if (mgrvo != null) { |
| ucsDao.remove(mgrvo.getId()); |
| } |
| throw e; |
| } |
| } |
| |
| private String getCookie(Long ucsMgrId) { |
| try { |
| UcsCookie ucsCookie = cookies.get(ucsMgrId); |
| long currentTime = System.currentTimeMillis(); |
| UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId); |
| UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); |
| String cmd = null; |
| if (ucsCookie == null) { |
| cmd = UcsCommands.loginCmd(mgrvo.getUsername(), mgrvo.getPassword()); |
| } else { |
| String cookie = ucsCookie.getCookie(); |
| long cookieStartTime = ucsCookie.getStartTime(); |
| if (currentTime - cookieStartTime > COOKIE_TTL) { |
| cmd = UcsCommands.loginCmd(mgrvo.getUsername(), mgrvo.getPassword()); |
| } else if (currentTime - cookieStartTime > COOKIE_REFRESH_TTL) { |
| cmd = UcsCommands.refreshCmd(mgrvo.getUsername(), mgrvo.getPassword(), cookie); |
| } |
| } |
| if (!(cmd == null)) { |
| String ret = client.call(cmd); |
| XmlObject xo = XmlObjectParser.parseFromString(ret); |
| String cookie = xo.get("outCookie"); |
| ucsCookie = new UcsCookie(cookie, currentTime); |
| cookies.put(ucsMgrId, ucsCookie); |
| //cookiesTime.put(cookie, currentTime); //This is currentTime on purpose, and not latest time. |
| } |
| return ucsCookie.getCookie(); |
| } catch (Exception e) { |
| throw new CloudRuntimeException("Cannot get cookie", e); |
| } |
| } |
| |
| private List<ComputeBlade> listBlades(Long ucsMgrId) { |
| String cookie = getCookie(ucsMgrId); |
| UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId); |
| UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); |
| String cmd = UcsCommands.listComputeBlades(cookie); |
| String ret = client.call(cmd); |
| return ComputeBlade.fromXmString(ret); |
| } |
| |
| private List<UcsProfile> getUcsProfiles(Long ucsMgrId) { |
| String cookie = getCookie(ucsMgrId); |
| UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId); |
| String cmd = UcsCommands.listProfiles(cookie); |
| UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); |
| String res = client.call(cmd); |
| List<UcsProfile> profiles = UcsProfile.fromXmlString(res); |
| return profiles; |
| } |
| |
| @Override |
| public ListResponse<UcsProfileResponse> listUcsProfiles(ListUcsProfileCmd cmd) { |
| List<UcsProfile> profiles = getUcsProfiles(cmd.getUcsManagerId()); |
| ListResponse<UcsProfileResponse> response = new ListResponse<UcsProfileResponse>(); |
| List<UcsProfileResponse> rs = new ArrayList<UcsProfileResponse>(); |
| for (UcsProfile p : profiles) { |
| UcsProfileResponse r = new UcsProfileResponse(); |
| r.setObjectName("ucsprofile"); |
| r.setDn(p.getDn()); |
| rs.add(r); |
| } |
| response.setResponses(rs); |
| return response; |
| } |
| |
| private String cloneProfile(Long ucsMgrId, String srcDn, String newProfileName) { |
| UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId); |
| UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); |
| String cookie = getCookie(ucsMgrId); |
| String cmd = UcsCommands.cloneProfile(cookie, srcDn, newProfileName); |
| String res = client.call(cmd); |
| XmlObject xo = XmlObjectParser.parseFromString(res); |
| return xo.get("outConfig.lsServer.dn"); |
| } |
| |
| private boolean isProfileAssociated(Long ucsMgrId, String dn) { |
| UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId); |
| UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); |
| String cookie = getCookie(ucsMgrId); |
| String cmd = UcsCommands.configResolveDn(cookie, dn); |
| String res = client.call(cmd); |
| XmlObject xo = XmlObjectParser.parseFromString(res); |
| s_logger.debug(String.format("association response is %s", res)); |
| |
| if (xo.get("outConfig.computeBlade.association").equals("none")) { |
| throw new CloudRuntimeException(String.format("cannot associated a profile to blade[dn:%s]. please check your UCS manasger for detailed error information", |
| dn)); |
| } |
| |
| return xo.get("outConfig.computeBlade.association").equals("associated"); |
| } |
| |
| @Override |
| public UcsBladeResponse associateProfileToBlade(AssociateUcsProfileToBladeCmd cmd) { |
| QueryBuilder<UcsBladeVO> q = QueryBuilder.create(UcsBladeVO.class); |
| q.and(q.entity().getUcsManagerId(), Op.EQ, cmd.getUcsManagerId()); |
| q.and(q.entity().getId(), Op.EQ, cmd.getBladeId()); |
| UcsBladeVO bvo = q.find(); |
| if (bvo == null) { |
| throw new IllegalArgumentException(String.format("cannot find UCS blade[id:%s, ucs manager id:%s]", cmd.getBladeId(), cmd.getUcsManagerId())); |
| } |
| |
| if (bvo.getHostId() != null) { |
| throw new CloudRuntimeException(String.format("blade[id:%s, dn:%s] has been associated with host[id:%s]", bvo.getId(), bvo.getDn(), bvo.getHostId())); |
| } |
| |
| UcsManagerVO mgrvo = ucsDao.findById(cmd.getUcsManagerId()); |
| String cookie = getCookie(cmd.getUcsManagerId()); |
| String pdn = cloneProfile(mgrvo.getId(), cmd.getProfileDn(), "profile-for-blade-" + bvo.getId()); |
| String ucscmd = UcsCommands.associateProfileToBlade(cookie, pdn, bvo.getDn()); |
| UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); |
| String res = client.call(ucscmd); |
| int count = 0; |
| int timeout = 600; |
| while (count < timeout) { |
| if (isProfileAssociated(mgrvo.getId(), bvo.getDn())) { |
| break; |
| } |
| |
| try { |
| TimeUnit.SECONDS.sleep(2); |
| } catch (InterruptedException e) { |
| throw new CloudRuntimeException(e); |
| } |
| |
| count += 2; |
| } |
| |
| if (count >= timeout) { |
| throw new CloudRuntimeException(String.format("associating profile[%s] to balde[%s] timeout after 600 seconds", pdn, bvo.getDn())); |
| } |
| |
| bvo.setProfileDn(pdn); |
| bladeDao.update(bvo.getId(), bvo); |
| |
| UcsBladeResponse rsp = bladeVOToResponse(bvo); |
| |
| s_logger.debug(String.format("successfully associated profile[%s] to blade[%s]", pdn, bvo.getDn())); |
| return rsp; |
| } |
| |
| private String hostIdToUuid(Long hostId) { |
| if (hostId == null) { |
| return null; |
| } |
| HostVO vo = hostDao.findById(hostId); |
| return vo.getUuid(); |
| } |
| |
| private String zoneIdToUuid(Long zoneId) { |
| DataCenterVO vo = dcDao.findById(zoneId); |
| return vo.getUuid(); |
| } |
| |
| private String ucsManagerIdToUuid(Long ucsMgrId) { |
| UcsManagerVO vo = ucsDao.findById(ucsMgrId); |
| return vo.getUuid(); |
| } |
| |
| @Override |
| public ListResponse<UcsManagerResponse> listUcsManager(ListUcsManagerCmd cmd) { |
| List<UcsManagerResponse> rsps = new ArrayList<UcsManagerResponse>(); |
| ListResponse<UcsManagerResponse> response = new ListResponse<UcsManagerResponse>(); |
| if (cmd.getId() != null) { |
| UcsManagerVO vo = ucsDao.findById(cmd.getId()); |
| UcsManagerResponse rsp = new UcsManagerResponse(); |
| rsp.setObjectName("ucsmanager"); |
| rsp.setId(vo.getUuid()); |
| rsp.setName(vo.getName()); |
| rsp.setUrl(vo.getUrl()); |
| rsp.setZoneId(zoneIdToUuid(vo.getZoneId())); |
| rsps.add(rsp); |
| response.setResponses(rsps); |
| return response; |
| } |
| |
| QueryBuilder<UcsManagerVO> serv = QueryBuilder.create(UcsManagerVO.class); |
| serv.and(serv.entity().getZoneId(), Op.EQ, cmd.getZoneId()); |
| List<UcsManagerVO> vos = serv.list(); |
| |
| for (UcsManagerVO vo : vos) { |
| UcsManagerResponse rsp = new UcsManagerResponse(); |
| rsp.setObjectName("ucsmanager"); |
| rsp.setId(vo.getUuid()); |
| rsp.setName(vo.getName()); |
| rsp.setUrl(vo.getUrl()); |
| rsp.setZoneId(zoneIdToUuid(vo.getZoneId())); |
| rsps.add(rsp); |
| } |
| response.setResponses(rsps); |
| return response; |
| } |
| |
| private UcsBladeResponse bladeVOToResponse(UcsBladeVO vo) { |
| UcsBladeResponse rsp = new UcsBladeResponse(); |
| rsp.setObjectName("ucsblade"); |
| rsp.setId(vo.getUuid()); |
| rsp.setDn(vo.getDn()); |
| rsp.setHostId(hostIdToUuid(vo.getHostId())); |
| rsp.setAssociatedProfileDn(vo.getProfileDn()); |
| rsp.setUcsManagerId(ucsManagerIdToUuid(vo.getUcsManagerId())); |
| return rsp; |
| } |
| |
| @Override |
| public ListResponse<UcsBladeResponse> listUcsBlades(ListUcsBladeCmd cmd) { |
| QueryBuilder<UcsBladeVO> serv = QueryBuilder.create(UcsBladeVO.class); |
| serv.and(serv.entity().getUcsManagerId(), Op.EQ, cmd.getUcsManagerId()); |
| List<UcsBladeVO> vos = serv.list(); |
| |
| List<UcsBladeResponse> rsps = new ArrayList<UcsBladeResponse>(vos.size()); |
| for (UcsBladeVO vo : vos) { |
| UcsBladeResponse rsp = bladeVOToResponse(vo); |
| rsps.add(rsp); |
| } |
| |
| ListResponse<UcsBladeResponse> response = new ListResponse<UcsBladeResponse>(); |
| response.setResponses(rsps); |
| |
| return response; |
| } |
| |
| @Override |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| @Override |
| public void setConfigParams(Map<String, Object> params) { |
| this.params = params; |
| } |
| |
| @Override |
| public Map<String, Object> getConfigParams() { |
| return params; |
| } |
| |
| @Override |
| public int getRunLevel() { |
| return runLevel; |
| } |
| |
| @Override |
| public void setRunLevel(int level) { |
| runLevel = level; |
| } |
| |
| @Override |
| public List<Class<?>> getCommands() { |
| List<Class<?>> cmds = new ArrayList<Class<?>>(); |
| cmds.add(ListUcsBladeCmd.class); |
| cmds.add(ListUcsManagerCmd.class); |
| cmds.add(ListUcsProfileCmd.class); |
| cmds.add(AddUcsManagerCmd.class); |
| cmds.add(AssociateUcsProfileToBladeCmd.class); |
| cmds.add(DeleteUcsManagerCmd.class); |
| return cmds; |
| } |
| |
| @Override |
| public void deleteUcsManager(Long id) { |
| QueryBuilder<UcsBladeVO> serv = QueryBuilder.create(UcsBladeVO.class); |
| serv.and(serv.entity().getUcsManagerId(), Op.EQ, id); |
| List<UcsBladeVO> vos = serv.list(); |
| for (UcsBladeVO vo : vos) { |
| bladeDao.remove(vo.getId()); |
| } |
| ucsDao.remove(id); |
| } |
| } |