// 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.baremetal.manager; import com.cloud.baremetal.database.BaremetalRctDao; import com.cloud.baremetal.database.BaremetalRctVO; import com.cloud.baremetal.networkservice.BaremetalRctResponse; import com.cloud.baremetal.networkservice.BaremetalSwitchBackend; import com.cloud.baremetal.networkservice.BaremetalVlanStruct; import com.cloud.deploy.DeployDestination; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.network.Network; import com.cloud.network.Networks; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; import com.cloud.user.User; import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachineProfile; import com.google.gson.Gson; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.AddBaremetalRctCmd; import org.apache.cloudstack.api.DeleteBaremetalRctCmd; import org.apache.cloudstack.api.ListBaremetalRctCmd; import org.apache.cloudstack.utils.baremetal.BaremetalUtils; import org.springframework.web.client.RestTemplate; import javax.inject.Inject; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; /** * Created by frank on 5/8/14. */ public class BaremetalVlanManagerImpl extends ManagerBase implements BaremetalVlanManager { private Gson gson = new Gson(); @Inject private BaremetalRctDao rctDao; @Inject private HostDao hostDao; @Inject private AccountDao acntDao; @Inject private UserDao userDao; @Inject private AccountManager acntMgr; private Map<String, BaremetalSwitchBackend> backends; private class RackPair { BaremetalRct.Rack rack; BaremetalRct.HostEntry host; } public void setBackends(Map<String, BaremetalSwitchBackend> backends) { this.backends = backends; } @Override public BaremetalRctResponse addRct(AddBaremetalRctCmd cmd) { try { List<BaremetalRctVO> existings = rctDao.listAll(); if (!existings.isEmpty()) { throw new CloudRuntimeException(String.format("there is some RCT existing. A CloudStack deployment accepts only one RCT")); } URL url = new URL(cmd.getRctUrl()); RestTemplate rest = new RestTemplate(); String rctStr = rest.getForObject(url.toString(), String.class); // validate it's right format BaremetalRct rct = gson.fromJson(rctStr, BaremetalRct.class); QueryBuilder<BaremetalRctVO> sc = QueryBuilder.create(BaremetalRctVO.class); sc.and(sc.entity().getUrl(), SearchCriteria.Op.EQ, cmd.getRctUrl()); BaremetalRctVO vo = sc.find(); if (vo == null) { vo = new BaremetalRctVO(); vo.setRct(gson.toJson(rct)); vo.setUrl(cmd.getRctUrl()); vo = rctDao.persist(vo); } else { vo.setRct(gson.toJson(rct)); rctDao.update(vo.getId(), vo); } BaremetalRctResponse rsp = new BaremetalRctResponse(); rsp.setUrl(vo.getUrl()); rsp.setId(vo.getUuid()); rsp.setObjectName("baremetalrct"); return rsp; } catch (MalformedURLException e) { throw new IllegalArgumentException(String.format("%s is not a legal http url", cmd.getRctUrl())); } } @Override public void prepareVlan(Network nw, DeployDestination destHost) { List<BaremetalRctVO> vos = rctDao.listAll(); if (vos.isEmpty()) { throw new CloudRuntimeException("no rack configuration found, please call addBaremetalRct to add one"); } BaremetalRctVO vo = vos.get(0); BaremetalRct rct = gson.fromJson(vo.getRct(), BaremetalRct.class); RackPair rp = findRack(rct, destHost.getHost().getPrivateMacAddress()); if (rp == null) { throw new CloudRuntimeException(String.format("cannot find any rack contains host[mac:%s], please double check your rack configuration file, update it and call addBaremetalRct again", destHost.getHost().getPrivateMacAddress())); } int vlan = Integer.parseInt(Networks.BroadcastDomainType.getValue(nw.getBroadcastUri())); BaremetalSwitchBackend backend = getSwitchBackend(rp.rack.getL2Switch().getType()); BaremetalVlanStruct struct = new BaremetalVlanStruct(); struct.setHostMac(rp.host.getMac()); struct.setPort(rp.host.getPort()); struct.setSwitchIp(rp.rack.getL2Switch().getIp()); struct.setSwitchPassword(rp.rack.getL2Switch().getPassword()); struct.setSwitchType(rp.rack.getL2Switch().getType()); struct.setSwitchUsername(rp.rack.getL2Switch().getUsername()); struct.setVlan(vlan); backend.prepareVlan(struct); } @Override public void releaseVlan(Network nw, VirtualMachineProfile vm) { List<BaremetalRctVO> vos = rctDao.listAll(); if (vos.isEmpty()) { throw new CloudRuntimeException("no rack configuration found, please call addBaremetalRct to add one"); } BaremetalRctVO vo = vos.get(0); BaremetalRct rct = gson.fromJson(vo.getRct(), BaremetalRct.class); HostVO host = hostDao.findById(vm.getVirtualMachine().getHostId()); RackPair rp = findRack(rct, host.getPrivateMacAddress()); assert rp != null : String.format("where is my rack???"); int vlan = Integer.parseInt(Networks.BroadcastDomainType.getValue(nw.getBroadcastUri())); BaremetalVlanStruct struct = new BaremetalVlanStruct(); struct.setHostMac(rp.host.getMac()); struct.setPort(rp.host.getPort()); struct.setSwitchIp(rp.rack.getL2Switch().getIp()); struct.setSwitchPassword(rp.rack.getL2Switch().getPassword()); struct.setSwitchType(rp.rack.getL2Switch().getType()); struct.setSwitchUsername(rp.rack.getL2Switch().getUsername()); struct.setVlan(vlan); BaremetalSwitchBackend backend = getSwitchBackend(rp.rack.getL2Switch().getType()); backend.removePortFromVlan(struct); } @Override public void registerSwitchBackend(BaremetalSwitchBackend backend) { backends.put(backend.getSwitchBackendType(), backend); } @Override public void deleteRct(DeleteBaremetalRctCmd cmd) { rctDao.remove(cmd.getId()); } @Override public BaremetalRctResponse listRct() { List<BaremetalRctVO> vos = rctDao.listAll(); if (!vos.isEmpty()) { BaremetalRctVO vo = vos.get(0); BaremetalRctResponse rsp = new BaremetalRctResponse(); rsp.setId(vo.getUuid()); rsp.setUrl(vo.getUrl()); rsp.setObjectName("baremetalrct"); return rsp; } return null; } private BaremetalSwitchBackend getSwitchBackend(String type) { BaremetalSwitchBackend backend = backends.get(type); if (backend == null) { throw new CloudRuntimeException(String.format("cannot find switch backend[type:%s]", type)); } return backend; } private RackPair findRack(BaremetalRct rct, String mac) { for (BaremetalRct.Rack rack : rct.getRacks()) { for (BaremetalRct.HostEntry host : rack.getHosts()) { if (mac.equals(host.getMac())) { RackPair p = new RackPair(); p.host = host; p.rack = rack; return p; } } } return null; } @Override public String getName() { return "Baremetal Vlan Manager"; } @Override public List<Class<?>> getCommands() { List<Class<?>> cmds = new ArrayList<Class<?>>(); cmds.add(AddBaremetalRctCmd.class); cmds.add(ListBaremetalRctCmd.class); cmds.add(DeleteBaremetalRctCmd.class); return cmds; } @Override public boolean start() { QueryBuilder<AccountVO> acntq = QueryBuilder.create(AccountVO.class); acntq.and(acntq.entity().getAccountName(), SearchCriteria.Op.EQ, BaremetalUtils.BAREMETAL_SYSTEM_ACCOUNT_NAME); AccountVO acnt = acntq.find(); if (acnt != null) { return true; } acnt = new AccountVO(); acnt.setAccountName(BaremetalUtils.BAREMETAL_SYSTEM_ACCOUNT_NAME); acnt.setUuid(UUID.randomUUID().toString()); acnt.setState(Account.State.enabled); acnt.setDomainId(1); acnt.setType(RoleType.User.getAccountType()); acnt.setRoleId(RoleType.User.getId()); acnt = acntDao.persist(acnt); UserVO user = new UserVO(); user.setState(Account.State.enabled); user.setUuid(UUID.randomUUID().toString()); user.setAccountId(acnt.getAccountId()); user.setUsername(BaremetalUtils.BAREMETAL_SYSTEM_ACCOUNT_NAME); user.setFirstname(BaremetalUtils.BAREMETAL_SYSTEM_ACCOUNT_NAME); user.setLastname(BaremetalUtils.BAREMETAL_SYSTEM_ACCOUNT_NAME); user.setPassword(UUID.randomUUID().toString()); user.setSource(User.Source.UNKNOWN); user = userDao.persist(user); String[] keys = acntMgr.createApiKeyAndSecretKey(user.getId()); user.setApiKey(keys[0]); user.setSecretKey(keys[1]); userDao.update(user.getId(), user); return true; } }