mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
bug 10736: when deploy vm in Basic zone, 1) send vmData/Password info to the Dhcp running in the same pod 2) send dhcp information to all pods in the system only when network.dns.basiczone.updates has "all" value
status 10736: resolved fixed
This commit is contained in:
parent
37c1401ad3
commit
3167c49687
@ -238,8 +238,9 @@ public enum Config {
|
||||
SubDomainNetworkAccess("Advanced", NetworkManager.class, Boolean.class, "allow.subdomain.network.access", "true", "Allow subdomains to use networks dedicated to their parent domain(s)", null),
|
||||
EncodeApiResponse("Advanced", ManagementServer.class, Boolean.class, "encode.api.response", "false", "Do UTF-8 encoding for the api response, false by default", null),
|
||||
|
||||
ClusterMessageTimeOutSeconds("Advanced", ManagementServer.class, Integer.class, "cluster.message.timeout.seconds", "300", "Time (in seconds) to wait before a inter-management server message post times out.", null);
|
||||
|
||||
ClusterMessageTimeOutSeconds("Advanced", ManagementServer.class, Integer.class, "cluster.message.timeout.seconds", "300", "Time (in seconds) to wait before a inter-management server message post times out.", null),
|
||||
DnsBasicZoneUpdates("Advanced", NetworkManager.class, String.class, "network.dns.basiczone.updates", "all", "This parameter can take 2 values: all (default) and pod. It defines if DHCP/DNS requests have to be send to all dhcp servers in cloudstack, or only to the one in the same pod", "all,pod");
|
||||
|
||||
private final String _category;
|
||||
private final Class<?> _componentClass;
|
||||
private final Class<?> _type;
|
||||
|
||||
@ -276,6 +276,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
||||
int _routerStatsInterval = 300;
|
||||
private ServiceOfferingVO _offering;
|
||||
private String trafficSentinelHostname;
|
||||
private String _dnsBasicZoneUpdates = "all";
|
||||
|
||||
ScheduledExecutorService _executor;
|
||||
|
||||
@ -374,8 +375,8 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
||||
|
||||
@Override
|
||||
public boolean savePasswordToRouter(Network network, NicProfile nic, VirtualMachineProfile<UserVm> profile) throws ResourceUnavailableException {
|
||||
DomainRouterVO router = _routerDao.findByNetwork(network.getId());
|
||||
if (router == null) {
|
||||
List<DomainRouterVO> routers = _routerDao.listByNetworkAndState(network.getId(), null);
|
||||
if (routers == null || routers.isEmpty()) {
|
||||
s_logger.warn("Unable save password, router doesn't exist in network " + network.getId());
|
||||
throw new CloudRuntimeException("Unable to save password to router");
|
||||
}
|
||||
@ -383,19 +384,28 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
||||
UserVm userVm = profile.getVirtualMachine();
|
||||
String password = (String) profile.getParameter(Param.VmPassword);
|
||||
String encodedPassword = PasswordGenerator.rot13(password);
|
||||
DataCenter dc = _dcDao.findById(userVm.getDataCenterIdToDeployIn());
|
||||
|
||||
Commands cmds = new Commands(OnError.Continue);
|
||||
SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), userVm.getHostName());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
boolean result = true;
|
||||
for (DomainRouterVO router : routers) {
|
||||
boolean sendPassword = true;
|
||||
if (dc.getNetworkType() == NetworkType.Basic && userVm.getPodIdToDeployIn().longValue() != router.getPodIdToDeployIn().longValue()) {
|
||||
sendPassword = false;
|
||||
}
|
||||
|
||||
if (sendPassword) {
|
||||
Commands cmds = new Commands(OnError.Continue);
|
||||
SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), userVm.getHostName());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
|
||||
cmds.addCommand("password", cmd);
|
||||
|
||||
DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
|
||||
|
||||
cmds.addCommand("password", cmd);
|
||||
|
||||
return sendCommandsToRouter(router, cmds);
|
||||
result = result && sendCommandsToRouter(router, cmds);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override @ActionEvent(eventType = EventTypes.EVENT_ROUTER_STOP, eventDescription = "stopping router Vm", async = true)
|
||||
@ -562,7 +572,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
||||
_systemAcct = _accountService.getSystemAccount();
|
||||
|
||||
trafficSentinelHostname = configs.get("traffic.sentinel.hostname");
|
||||
|
||||
|
||||
_dnsBasicZoneUpdates = String.valueOf(_configDao.getValue(Config.DnsBasicZoneUpdates.key()));
|
||||
|
||||
s_logger.info("DomainRouterManager is configured.");
|
||||
|
||||
return true;
|
||||
@ -1213,78 +1225,105 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
||||
public VirtualRouter addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile<UserVm> profile, DeployDestination dest, ReservationContext context, Boolean startDhcp)
|
||||
throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
|
||||
|
||||
DomainRouterVO router = startDhcp ? deployDhcp(network, dest, profile.getOwner(), profile.getParameters()) : deployVirtualRouter(network, dest, profile.getOwner(), profile.getParameters());
|
||||
DomainRouterVO startedRouter = startDhcp ? deployDhcp(network, dest, profile.getOwner(), profile.getParameters()) : deployVirtualRouter(network, dest, profile.getOwner(), profile.getParameters());
|
||||
List<DomainRouterVO> routers = _routerDao.listByNetworkAndState(network.getId(), State.Running);
|
||||
|
||||
|
||||
for (DomainRouterVO router : routers) {
|
||||
boolean sendPasswordAndVmData = true;
|
||||
boolean sendDnsDhcpData = true;
|
||||
_userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine());
|
||||
|
||||
//for basic zone:
|
||||
//1) send vm data/password information only to the dhcp in the same pod
|
||||
//2) send dhcp/dns information to all routers in the cloudstack only when _dnsBasicZoneUpdates is set to "all" value
|
||||
DataCenter dc = dest.getDataCenter();
|
||||
if (dc.getNetworkType() == NetworkType.Basic) {
|
||||
Long podId = dest.getPod().getId();
|
||||
if (router.getPodIdToDeployIn().longValue() != podId.longValue()) {
|
||||
sendPasswordAndVmData = false;
|
||||
if (_dnsBasicZoneUpdates.equalsIgnoreCase("pod")) {
|
||||
sendDnsDhcpData = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine());
|
||||
Commands cmds = new Commands(OnError.Stop);
|
||||
|
||||
String password = (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword);
|
||||
String userData = profile.getVirtualMachine().getUserData();
|
||||
String sshPublicKey = profile.getVirtualMachine().getDetail("SSH.PublicKey");
|
||||
Commands cmds = new Commands(OnError.Stop);
|
||||
String routerControlIpAddress = null;
|
||||
List<NicVO> nics = _nicDao.listByVmId(router.getId());
|
||||
for (NicVO n : nics) {
|
||||
NetworkVO nc = _networkDao.findById(n.getNetworkId());
|
||||
if (nc.getTrafficType() == TrafficType.Control) {
|
||||
routerControlIpAddress = n.getIp4Address();
|
||||
}
|
||||
}
|
||||
|
||||
String routerControlIpAddress = null;
|
||||
List<NicVO> nics = _nicDao.listByVmId(router.getId());
|
||||
for (NicVO n : nics) {
|
||||
NetworkVO nc = _networkDao.findById(n.getNetworkId());
|
||||
if (nc.getTrafficType() == TrafficType.Control) {
|
||||
routerControlIpAddress = n.getIp4Address();
|
||||
if (sendDnsDhcpData) {
|
||||
DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, routerControlIpAddress);
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dc.getNetworkType().toString());
|
||||
|
||||
cmds.addCommand("dhcp", dhcpCommand);
|
||||
}
|
||||
|
||||
if (sendPasswordAndVmData) {
|
||||
String password = (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword);
|
||||
String userData = profile.getVirtualMachine().getUserData();
|
||||
String sshPublicKey = profile.getVirtualMachine().getDetail("SSH.PublicKey");
|
||||
|
||||
// password should be set only on default network element
|
||||
if (password != null && network.isDefault()) {
|
||||
final String encodedPassword = PasswordGenerator.rot13(password);
|
||||
SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), profile.getVirtualMachine().getHostName());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dc.getNetworkType().toString());
|
||||
|
||||
cmds.addCommand("password", cmd);
|
||||
}
|
||||
|
||||
String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(profile.getServiceOfferingId()).getDisplayText();
|
||||
String zoneName = _dcDao.findById(network.getDataCenterId()).getName();
|
||||
|
||||
cmds.addCommand(
|
||||
"vmdata",
|
||||
generateVmDataCommand(router, nic.getIp4Address(), userData, serviceOffering, zoneName, nic.getIp4Address(), profile.getVirtualMachine().getHostName(), profile.getVirtualMachine()
|
||||
.getInstanceName(), profile.getId(), sshPublicKey));
|
||||
}
|
||||
|
||||
if (cmds.size() > 0) {
|
||||
try {
|
||||
_agentMgr.send(router.getHostId(), cmds);
|
||||
} catch (OperationTimedoutException e) {
|
||||
throw new AgentUnavailableException("Unable to reach the agent ", router.getHostId(), e);
|
||||
}
|
||||
|
||||
Answer answer = cmds.getAnswer("dhcp");
|
||||
if (!answer.getResult()) {
|
||||
s_logger.error("Unable to set dhcp entry for " + profile + " on domR: " + router.getHostName() + " due to " + answer.getDetails());
|
||||
throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn());
|
||||
}
|
||||
|
||||
answer = cmds.getAnswer("password");
|
||||
if (answer != null && !answer.getResult()) {
|
||||
s_logger.error("Unable to set password for " + profile + " due to " + answer.getDetails());
|
||||
throw new ResourceUnavailableException("Unable to set password due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn());
|
||||
}
|
||||
|
||||
answer = cmds.getAnswer("vmdata");
|
||||
if (answer != null && !answer.getResult()) {
|
||||
s_logger.error("Unable to set VM data for " + profile + " due to " + answer.getDetails());
|
||||
throw new ResourceUnavailableException("Unable to set VM data due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, routerControlIpAddress);
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
|
||||
DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
|
||||
|
||||
cmds.addCommand("dhcp", dhcpCommand);
|
||||
|
||||
// password should be set only on default network element
|
||||
if (password != null && network.isDefault()) {
|
||||
final String encodedPassword = PasswordGenerator.rot13(password);
|
||||
SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), profile.getVirtualMachine().getHostName());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
|
||||
|
||||
cmds.addCommand("password", cmd);
|
||||
}
|
||||
|
||||
String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(profile.getServiceOfferingId()).getDisplayText();
|
||||
String zoneName = _dcDao.findById(network.getDataCenterId()).getName();
|
||||
|
||||
cmds.addCommand(
|
||||
"vmdata",
|
||||
generateVmDataCommand(router, nic.getIp4Address(), userData, serviceOffering, zoneName, nic.getIp4Address(), profile.getVirtualMachine().getHostName(), profile.getVirtualMachine()
|
||||
.getInstanceName(), profile.getId(), sshPublicKey));
|
||||
|
||||
try {
|
||||
_agentMgr.send(router.getHostId(), cmds);
|
||||
} catch (OperationTimedoutException e) {
|
||||
throw new AgentUnavailableException("Unable to reach the agent ", router.getHostId(), e);
|
||||
}
|
||||
|
||||
Answer answer = cmds.getAnswer("dhcp");
|
||||
if (!answer.getResult()) {
|
||||
s_logger.error("Unable to set dhcp entry for " + profile + " on domR: " + router.getHostName() + " due to " + answer.getDetails());
|
||||
throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn());
|
||||
}
|
||||
|
||||
answer = cmds.getAnswer("password");
|
||||
if (answer != null && !answer.getResult()) {
|
||||
s_logger.error("Unable to set password for " + profile + " due to " + answer.getDetails());
|
||||
throw new ResourceUnavailableException("Unable to set password due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn());
|
||||
}
|
||||
|
||||
answer = cmds.getAnswer("vmdata");
|
||||
if (answer != null && !answer.getResult()) {
|
||||
s_logger.error("Unable to set VM data for " + profile + " due to " + answer.getDetails());
|
||||
throw new ResourceUnavailableException("Unable to set VM data due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn());
|
||||
}
|
||||
return router;
|
||||
return startedRouter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1484,34 +1523,49 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
||||
|
||||
private void createVmDataCommands(DomainRouterVO router, Commands cmds) {
|
||||
long networkId = router.getNetworkId();
|
||||
List<UserVmVO> vms = _userVmDao.listByNetworkIdAndStates(networkId, State.Starting, State.Running, State.Migrating, State.Stopping);
|
||||
List<UserVmVO> vms = _userVmDao.listByNetworkIdAndStates(networkId, State.Running, State.Migrating, State.Stopping);
|
||||
DataCenterVO dc = _dcDao.findById(router.getDataCenterIdToDeployIn());
|
||||
for (UserVmVO vm : vms) {
|
||||
NicVO nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId());
|
||||
if (nic != null) {
|
||||
s_logger.debug("Creating user data entry for vm " + vm + " on domR " + router);
|
||||
String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()).getDisplayText();
|
||||
String zoneName = _dcDao.findById(router.getDataCenterIdToDeployIn()).getName();
|
||||
cmds.addCommand("vmdata",
|
||||
generateVmDataCommand(router, nic.getIp4Address(), vm.getUserData(), serviceOffering, zoneName, nic.getIp4Address(), vm.getHostName(), vm.getInstanceName(), vm.getId(), null));
|
||||
boolean createVmData = true;
|
||||
if (dc.getNetworkType() == NetworkType.Basic && router.getPodIdToDeployIn().longValue() != vm.getPodIdToDeployIn().longValue()) {
|
||||
createVmData = false;
|
||||
}
|
||||
|
||||
if (createVmData) {
|
||||
NicVO nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId());
|
||||
if (nic != null) {
|
||||
s_logger.debug("Creating user data entry for vm " + vm + " on domR " + router);
|
||||
String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()).getDisplayText();
|
||||
String zoneName = _dcDao.findById(router.getDataCenterIdToDeployIn()).getName();
|
||||
cmds.addCommand("vmdata",
|
||||
generateVmDataCommand(router, nic.getIp4Address(), vm.getUserData(), serviceOffering, zoneName, nic.getIp4Address(), vm.getHostName(), vm.getInstanceName(), vm.getId(), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createDhcpEntriesCommands(DomainRouterVO router, Commands cmds) {
|
||||
long networkId = router.getNetworkId();
|
||||
List<UserVmVO> vms = _userVmDao.listByNetworkIdAndStates(networkId, State.Starting, State.Running, State.Migrating, State.Stopping);
|
||||
List<UserVmVO> vms = _userVmDao.listByNetworkIdAndStates(networkId, State.Running, State.Migrating, State.Stopping);
|
||||
DataCenterVO dc = _dcDao.findById(router.getDataCenterIdToDeployIn());
|
||||
for (UserVmVO vm : vms) {
|
||||
NicVO nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId());
|
||||
if (nic != null) {
|
||||
s_logger.debug("Creating dhcp entry for vm " + vm + " on domR " + router + ".");
|
||||
boolean createDhcp = true;
|
||||
if (dc.getNetworkType() == NetworkType.Basic && router.getPodIdToDeployIn().longValue() != vm.getPodIdToDeployIn().longValue() && _dnsBasicZoneUpdates.equalsIgnoreCase("pod")) {
|
||||
createDhcp = false;
|
||||
}
|
||||
if (createDhcp) {
|
||||
NicVO nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId());
|
||||
if (nic != null) {
|
||||
s_logger.debug("Creating dhcp entry for vm " + vm + " on domR " + router + ".");
|
||||
|
||||
DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), vm.getHostName());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
|
||||
DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), vm.getHostName());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
|
||||
DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn());
|
||||
dhcpCommand.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
|
||||
|
||||
cmds.addCommand("dhcp", dhcpCommand);
|
||||
cmds.addCommand("dhcp", dhcpCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,4 +91,6 @@ public interface DomainRouterDao extends GenericDao<DomainRouterVO, Long> {
|
||||
* @return
|
||||
*/
|
||||
List<DomainRouterVO> listByStateAndNetworkType(State state, GuestIpType ipType);
|
||||
|
||||
List<DomainRouterVO> listByNetworkAndState(long networkId, State state);
|
||||
}
|
||||
|
||||
@ -194,4 +194,14 @@ public class DomainRouterDaoImpl extends GenericDaoBase<DomainRouterVO, Long> im
|
||||
sc.setJoinParameters("network", "guestType", ipType);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DomainRouterVO> listByNetworkAndState(long networkId, State state) {
|
||||
SearchCriteria<DomainRouterVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("network", networkId);
|
||||
if (state != null) {
|
||||
sc.setParameters("state", state);
|
||||
}
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user