content: // 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;
      }
  }
  