// 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 org.apache.cloudstack.network.contrail.model; import java.io.IOException; import java.text.MessageFormat; import java.util.List; import java.util.Map; import java.util.TreeSet; import net.juniper.contrail.api.ApiConnector; import net.juniper.contrail.api.types.Project; import net.juniper.contrail.api.types.ServiceInstance; import net.juniper.contrail.api.types.VirtualMachine; import org.apache.cloudstack.network.contrail.management.ContrailManager; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.cloud.exception.InternalErrorException; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.uservm.UserVm; import com.cloud.utils.UuidUtils; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.NicDao; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; public class VirtualMachineModel extends ModelObjectBase { private static final Logger s_logger = Logger.getLogger(VirtualMachineModel.class); private final String _uuid; private long _instanceId; /* * current state for object properties */ private boolean _initialized; private boolean _active; private String _serviceUuid; private String _instanceName; private String _projectId; /* * cached API server objects */ private VirtualMachine _vm; private ServiceInstanceModel _serviceModel; public VirtualMachineModel(VMInstanceVO vm, String uuid) { _uuid = uuid; if (vm != null) { _instanceId = vm.getId(); _instanceName = vm.getInstanceName(); } } /** * Resynchronize internal state from the cloudstack DB object. * @param instance */ public void build(ModelController controller, VMInstanceVO instance) { setProperties(controller, instance); UserVm userVm = controller.getVmDao().findById(instance.getId()); if (userVm != null && userVm.getUserData() != null) { s_logger.debug("vm " + instance.getInstanceName() + " user data: " + userVm.getUserData()); final Gson json = new Gson(); Map<String, String> kvmap = json.fromJson(userVm.getUserData(), new TypeToken<Map<String, String>>() { }.getType()); //Renamed "data" to "serviceUuid" because it's clearer. String serviceUuid = kvmap.get("service-instance"); if (serviceUuid != null) { /* * UUID.fromString() does not validate an UUID properly. I tried, for example, informing less digits in the UUID, where 12 were expected, * and the UUID.fromstring() did not thrown the exception as expected. However, if you try UUID.fromString("aaa") it breaks, but if you try * UUID.fromString("3dd4fa6e-2899-4429-b818-d34fe8df5") it doesn't (the last portion should have 12, instead of 9 digits). * * In other fix I added the validate UUID method to the UuidUtil classes. */ if (UuidUtils.validateUUID(serviceUuid)) { /* link the object with the service instance */ buildServiceInstance(controller, serviceUuid); } else { // Throw a CloudRuntimeException in case the UUID is not valid. String message = "Invalid UUID ({0}) given for the service-instance for VM {1}."; message = MessageFormat.format(message, instance.getId(), serviceUuid); s_logger.warn(message); throw new CloudRuntimeException(message); } } } } /** * Link the virtual machine with the service instance when recovering state from database. * * @param controller * @param serviceUuid */ private void buildServiceInstance(ModelController controller, String serviceUuid) { ContrailManager manager = controller.getManager(); ApiConnector api = controller.getApiAccessor(); _serviceUuid = serviceUuid; ServiceInstance siObj; try { siObj = (ServiceInstance) api.findById(ServiceInstance.class, serviceUuid); } catch (IOException ex) { s_logger.warn("service-instance read", ex); throw new CloudRuntimeException("Unable to read service-instance object", ex); } ServiceInstanceModel siModel; String fqn = StringUtils.join(siObj.getQualifiedName(), ':'); siModel = manager.getDatabase().lookupServiceInstance(fqn); if (siModel == null) { siModel = new ServiceInstanceModel(serviceUuid); siModel.build(controller, siObj); manager.getDatabase().getServiceInstances().add(siModel); } /* * The code that was under the ELSE was never executed and due to that has been removed. * Also, in the case siObj was null, it was going pass it as parameter to the build() method in the * siModel object. */ _serviceModel = siModel; } @Override public int compareTo(ModelObject o) { VirtualMachineModel other; try { other = (VirtualMachineModel)o; } catch (ClassCastException ex) { String clsname = o.getClass().getName(); return VirtualMachineModel.class.getName().compareTo(clsname); } return _uuid.compareTo(other._uuid); } @Override public void delete(ModelController controller) throws IOException { ApiConnector api = controller.getApiAccessor(); for (ModelObject successor : successors()) { successor.delete(controller); } try { api.delete(VirtualMachine.class, _uuid); } catch (IOException ex) { s_logger.warn("virtual-machine delete", ex); } if (_serviceModel != null) { _serviceModel.delete(controller); } } @Override public void destroy(ModelController controller) throws IOException { delete(controller); for (ModelObject successor : successors()) { successor.destroy(controller); } clearSuccessors(); if (_serviceModel != null) { _serviceModel.removeSuccessor(this); _serviceModel.destroy(controller); ContrailManager manager = controller.getManager(); manager.getDatabase().getServiceInstances().remove(_serviceModel); _serviceModel = null; } } public String getInstanceName() { return _instanceName; } public String getUuid() { return _uuid; } public VirtualMachine getVirtualMachine() { return _vm; } public VMInterfaceModel getVMInterface(String uuid) { TreeSet<ModelObject> tree = successors(); VMInterfaceModel vmiKey = new VMInterfaceModel(uuid); VMInterfaceModel current = (VMInterfaceModel)tree.ceiling(vmiKey); if (current != null && current.getUuid().equals(uuid)) { return current; } return null; } public boolean isActive() { return _active; } boolean isActiveInstance(VMInstanceVO instance) { switch (instance.getState()) { case Migrating: case Starting: case Running: case Shutdowned: case Stopped: case Stopping: return true; case Destroyed: case Error: case Expunging: return false; default: s_logger.warn("Unknown VMInstance state " + instance.getState().getDescription()); } return true; } /** * Initialize the object properties based on the DB object. * Common code between plugin calls and DBSync. */ public void setProperties(ModelController controller, VMInstanceVO instance) { ContrailManager manager = controller.getManager(); _instanceName = instance.getInstanceName(); _active = isActiveInstance(instance); try { _projectId = manager.getProjectId(instance.getDomainId(), instance.getAccountId()); } catch (IOException ex) { s_logger.warn("project read", ex); throw new CloudRuntimeException(ex); } _initialized = true; } /** * Link the virtual machine with a service instance via programmatic API call. * @throws IOException */ public void setServiceInstance(ModelController controller, VMInstanceVO instance, ServiceInstanceModel serviceModel) throws IOException { _serviceUuid = serviceModel.getUuid(); _serviceModel = serviceModel; serviceModel.addSuccessor(this); setServiceInstanceNics(controller, instance); } private void setServiceInstanceNics(ModelController controller, VMInstanceVO instance) throws IOException { NicDao nicDao = controller.getNicDao(); ContrailManager manager = controller.getManager(); NetworkDao networkDao = controller.getNetworkDao(); List<NicVO> nics = nicDao.listByVmId(_instanceId); for (NicVO nic : nics) { String tag; switch (nic.getDeviceId()) { case 0: tag = "management"; break; case 1: tag = "left"; break; case 2: tag = "right"; break; default: tag = null; } VMInterfaceModel vmiModel = getVMInterface(nic.getUuid()); if (vmiModel == null) { vmiModel = new VMInterfaceModel(nic.getUuid()); vmiModel.addToVirtualMachine(this); NetworkVO network = networkDao.findById(nic.getNetworkId()); VirtualNetworkModel vnModel = manager.getDatabase().lookupVirtualNetwork(network.getUuid(), manager.getCanonicalName(network), network.getTrafficType()); assert vnModel != null; vmiModel.addToVirtualNetwork(vnModel); } vmiModel.setProperties(controller, instance, nic); vmiModel.setServiceTag(tag); } } @Override public void update(ModelController controller) throws InternalErrorException, IOException { assert _initialized; ApiConnector api = controller.getApiAccessor(); VirtualMachine vm = _vm; if (vm == null) { _vm = vm = (VirtualMachine)api.findById(VirtualMachine.class, _uuid); if (vm == null) { vm = new VirtualMachine(); if (_projectId != null) { Project project; try { project = (Project)api.findById(Project.class, _projectId); } catch (IOException ex) { s_logger.debug("project read", ex); throw new CloudRuntimeException("Failed to read project", ex); } vm.setParent(project); } vm.setName(_instanceName); vm.setUuid(_uuid); } } if (_serviceModel != null) { vm.setServiceInstance(_serviceModel.getServiceInstance()); } if (_vm == null) { try { api.create(vm); } catch (Exception ex) { s_logger.debug("virtual-machine create", ex); throw new CloudRuntimeException("Failed to create virtual-machine", ex); } _vm = vm; } else { try { api.update(vm); } catch (IOException ex) { s_logger.warn("virtual-machine update", ex); throw new CloudRuntimeException("Unable to update virtual-machine object", ex); } } for (ModelObject successor : successors()) { successor.update(controller); } } @Override public boolean verify(ModelController controller) { assert _initialized : "initialized is false"; assert _uuid != null : "uuid is not set"; ApiConnector api = controller.getApiAccessor(); try { _vm = (VirtualMachine) api.findById(VirtualMachine.class, _uuid); } catch (IOException e) { s_logger.error("virtual-machine verify", e); } if (_vm == null) { return false; } for (ModelObject successor: successors()) { if (!successor.verify(controller)) { return false; } } return true; } @Override public boolean compare(ModelController controller, ModelObject current) { // TODO Auto-generated method stub return true; } }