HBASE-23 UI listing regions should be sorted by address and show additional region state

M  src/java/org/apache/hadoop/hbase/HServerLoad.java
    (toString): Overload that takes interval.
M  src/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
    (getConfiguration): Added
M  src/java/org/apache/hadoop/hbase/master/HMaster.java
    (getAverageLoad): Added
M src/java/org/apache/hadoop/hbase/HServerAddress.java
    (getHostname): Added.
M  src/java/org/apache/hadoop/hbase/client/HTable.java
    (getRegionsInfo): Added.
M  src/webapps/regionserver/regionserver.jsp
M  src/webapps/master/master.jsp
    Output requests as requests per second.
    Fixed up the help comments.  Changed all to use new getHostname
    rather than IPs.  Added encoded name column.
M  src/webapps/master/table.jsp
    Added.
M  src/webapps/master/WEB-INF/web.xml
    Add new tables servlet.
M  bin/hbase
    Put hbase in front of hadoop again.  FIxes not being able to find
    stylesheets -- might break logging again though doesnt seem to in
    tests.


git-svn-id: https://svn.apache.org/repos/asf/hadoop/hbase/trunk@656366 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael Stack 2008-05-14 19:06:52 +00:00
parent 595b75b105
commit 822ba8bc3b
10 changed files with 235 additions and 59 deletions

View File

@ -103,14 +103,6 @@ IFS=
# CLASSPATH initially contains $HBASE_CONF_DIR
CLASSPATH="${HBASE_CONF_DIR}"
# Add libs to CLASSPATH
# Do this early so hadoop jar comes before hbase classes; otherwise
# complaint because way webapps are loaded, expectation is that
# loading class is from same jar as webapps themselves (only true
# if hadoop comes first).
for f in $HBASE_HOME/lib/*.jar; do
CLASSPATH=${CLASSPATH}:$f;
done
CLASSPATH=${CLASSPATH}:$JAVA_HOME/lib/tools.jar
@ -125,7 +117,7 @@ if [ -d "$HBASE_HOME/build/webapps" ]; then
CLASSPATH=${CLASSPATH}:$HBASE_HOME/build
fi
# for releases, add hbase, hadoop & webapps to CLASSPATH
# for releases, add hbase & webapps to CLASSPATH
for f in $HBASE_HOME/hbase*.jar; do
if [ -f $f ]; then
CLASSPATH=${CLASSPATH}:$f;
@ -135,6 +127,11 @@ if [ -d "$HBASE_HOME/webapps" ]; then
CLASSPATH=${CLASSPATH}:$HBASE_HOME
fi
# Add libs to CLASSPATH
for f in $HBASE_HOME/lib/*.jar; do
CLASSPATH=${CLASSPATH}:$f;
done
for f in $HBASE_HOME/lib/jetty-ext/*.jar; do
CLASSPATH=${CLASSPATH}:$f;
done

View File

@ -89,7 +89,7 @@ public class HServerAddress implements WritableComparable {
stringValue = bindAddress + ":" + port;
}
/** @return host name */
/** @return bind address */
public String getBindAddress() {
return address.getAddress().getHostAddress();
}
@ -98,6 +98,11 @@ public class HServerAddress implements WritableComparable {
public int getPort() {
return address.getPort();
}
/** @return host name */
public String getHostname() {
return address.getHostName();
}
/** @return the InetSocketAddress */
public InetSocketAddress getInetSocketAddress() {

View File

@ -70,7 +70,16 @@ public class HServerLoad implements WritableComparable {
/** {@inheritDoc} */
@Override
public String toString() {
return "requests: " + numberOfRequests + " regions: " + numberOfRegions;
return toString(1);
}
/**
* Returns toString() with the number of requests divided by the message interval in seconds
* @param msgInterval
* @return The load as a String
*/
public String toString(int msgInterval) {
return "requests: " + numberOfRequests/msgInterval + " regions: " + numberOfRegions;
}
/** {@inheritDoc} */

View File

@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.client;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -32,6 +33,7 @@ import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.HServerAddress;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.filter.RowFilterInterface;
import org.apache.hadoop.hbase.filter.StopRowFilter;
@ -175,13 +177,76 @@ public class HTable implements HConstants {
throw e;
}
} while (startRow.compareTo(EMPTY_START_ROW) != 0);
Text[] arr = new Text[keyList.size()];
for (int i = 0; i < keyList.size(); i++ ){
arr[i] = keyList.get(i);
}
return arr;
return keyList.toArray(new Text[keyList.size()]);
}
/**
* Get all the regions and their address for this table
* @return A map of HRegionInfo with it's server address
* @throws IOException
*/
@SuppressWarnings("null")
public Map<HRegionInfo, HServerAddress> getRegionsInfo() throws IOException {
// TODO This code is a near exact copy of getStartKeys. To be refactored HBASE-626
HashMap<HRegionInfo, HServerAddress> regionMap = new HashMap<HRegionInfo, HServerAddress>();
long scannerId = -1L;
Text startRow = new Text(tableName.toString() + ",,999999999999999");
HRegionLocation metaLocation = null;
HRegionInterface server;
// scan over the each meta region
do {
try{
// turn the start row into a location
metaLocation =
connection.locateRegion(META_TABLE_NAME, startRow);
// connect to the server hosting the .META. region
server =
connection.getHRegionConnection(metaLocation.getServerAddress());
// open a scanner over the meta region
scannerId = server.openScanner(
metaLocation.getRegionInfo().getRegionName(),
new Text[]{COL_REGIONINFO}, tableName, LATEST_TIMESTAMP,
null);
// iterate through the scanner, accumulating regions and their regionserver
while (true) {
RowResult values = server.next(scannerId);
if (values == null || values.size() == 0) {
break;
}
HRegionInfo info = new HRegionInfo();
info = (HRegionInfo) Writables.getWritable(
values.get(COL_REGIONINFO).getValue(), info);
if (!info.getTableDesc().getName().equals(this.tableName)) {
break;
}
if (info.isOffline() || info.isSplit()) {
continue;
}
regionMap.put(info, metaLocation.getServerAddress());
}
// close that remote scanner
server.close(scannerId);
// advance the startRow to the end key of the current region
startRow = metaLocation.getRegionInfo().getEndKey();
} catch (IOException e) {
// need retry logic?
throw e;
}
} while (startRow.compareTo(EMPTY_START_ROW) != 0);
return regionMap;
}
/**

View File

@ -301,6 +301,11 @@ public class HMaster extends Thread implements HConstants, HMasterInterface,
public Map<String, HServerLoad> getServersToLoad() {
return serverManager.getServersToLoad();
}
/** @return The average load */
public double getAverageLoad() {
return serverManager.getAverageLoad();
}
/**
* @return Location of the <code>-ROOT-</code> region.

View File

@ -1266,6 +1266,14 @@ public class HRegionServer implements HConstants, HRegionInterface, Runnable {
public boolean isStopRequested() {
return stopRequested.get();
}
/**
*
* @return the configuration
*/
public HBaseConfiguration getConfiguration() {
return conf;
}
/** @return the write lock for the server */
ReentrantReadWriteLock.WriteLock getWriteLock() {

View File

@ -9,6 +9,11 @@ Automatically created by Tomcat JspC.
<web-app>
<servlet>
<servlet-name>org.apache.hadoop.hbase.generated.master.table_jsp</servlet-name>
<servlet-class>org.apache.hadoop.hbase.generated.master.table_jsp</servlet-class>
</servlet>
<servlet>
<servlet-name>org.apache.hadoop.hbase.generated.master.hql_jsp</servlet-name>
<servlet-class>org.apache.hadoop.hbase.generated.master.hql_jsp</servlet-class>
@ -19,6 +24,11 @@ Automatically created by Tomcat JspC.
<servlet-class>org.apache.hadoop.hbase.generated.master.master_jsp</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>org.apache.hadoop.hbase.generated.master.table_jsp</servlet-name>
<url-pattern>/table.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>org.apache.hadoop.hbase.generated.master.hql_jsp</servlet-name>
<url-pattern>/hql.jsp</url-pattern>

View File

@ -15,26 +15,24 @@
import="org.apache.hadoop.hbase.HTableDescriptor" %><%
HMaster master = (HMaster)getServletContext().getAttribute(HMaster.MASTER);
HBaseConfiguration conf = master.getConfiguration();
TableFormatter formatter = new HtmlTableFormatter(out);
ShowCommand show = new ShowCommand(out, formatter, "tables");
HServerAddress rootLocation = master.getRootRegionLocation();
Map<Text, MetaRegion> onlineRegions = master.getOnlineMetaRegions();
Map<String, HServerInfo> serverToServerInfos =
master.getServersToServerInfo();
int interval = conf.getInt("hbase.regionserver.msginterval", 6000)/1000;
int interval = conf.getInt("hbase.regionserver.msginterval", 3000)/1000;
%><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<title>HBase Master: <%= master.getMasterAddress()%></title>
<title>HBase Master: <%= master.getMasterAddress().getHostname()%>:<%= master.getMasterAddress().getPort() %></title>
<link rel="stylesheet" type="text/css" href="/static/hbase.css" />
</head>
<body>
<a id="logo" href="http://wiki.apache.org/lucene-hadoop/Hbase"><img src="/static/hbase_logo_med.gif" alt="HBase Logo" title="HBase Logo" /></a>
<h1 id="page_title">Master: <%=master.getMasterAddress()%></h1>
<h1 id="page_title">Master: <%=master.getMasterAddress().getHostname()%>:<%=master.getMasterAddress().getPort()%></h1>
<p id="links_menu"><a href="/hql.jsp">HQL</a>, <a href="/logs/">Local logs</a>, <a href="/stacks">Thread Dump</a>, <a href="/logLevel">Log Level</a></p>
<hr id="head_rule" />
@ -47,54 +45,61 @@
<tr><td>Hadoop Compiled</td><td><%= org.apache.hadoop.util.VersionInfo.getDate() %>, <%= org.apache.hadoop.util.VersionInfo.getUser() %></td><td>When Hadoop version was compiled and by whom</td></tr>
<tr><td>Filesystem</td><td><%= conf.get("fs.default.name") %></td><td>Filesystem HBase is running on</td></tr>
<tr><td>HBase Root Directory</td><td><%= master.getRootDir().toString() %></td><td>Location of HBase home directory</td></tr>
<tr><td>Load average</td><td><%= master.getAverageLoad() %></td><td>Average load across all region servers. Naive computation.</td></tr>
</table>
<h2>Online META Regions</h2>
<% if (rootLocation != null) { %>
<h2>Catalog Tables</h2>
<%
if (rootLocation != null) { %>
<table>
<tr><th>Name</th><th>Server</th></tr>
<tr><td><%= HConstants.ROOT_TABLE_NAME.toString() %></td><td><%= rootLocation.toString() %></td></tr>
<tr><th>Table</th><th>Description</th></tr>
<tr><td><a href=/table.jsp?name=<%= HConstants.ROOT_TABLE_NAME.toString() %>><%= HConstants.ROOT_TABLE_NAME.toString() %></a></td><td>The -ROOT- table holds references to all .META. regions.</td></tr>
<%
if (onlineRegions != null && onlineRegions.size() > 0) { %>
<% for (Map.Entry<Text, MetaRegion> e: onlineRegions.entrySet()) {
MetaRegion meta = e.getValue();
%>
<tr><td><%= meta.getRegionName().toString() %></td><td><%= meta.getServer().toString() %></td></tr>
<% }
} %>
if (onlineRegions != null && onlineRegions.size() > 0) { %>
<tr><td><a href=/table.jsp?name=<%= HConstants.META_TABLE_NAME.toString() %>><%= HConstants.META_TABLE_NAME.toString() %></a></td><td>The .META. table holds references to all User Table regions</td></tr>
<% } %>
</table>
<%} %>
<h2>User Tables</h2>
<% HTableDescriptor[] tables = new HBaseAdmin(conf).listTables();
if(tables != null && tables.length > 0) { %>
<table>
<tr><th>Table</th><th>Description</th></tr>
<% for(HTableDescriptor htDesc : tables ) { %>
<tr><td><a href=/table.jsp?name=<%= htDesc.getName() %>><%= htDesc.getName() %></a> </td><td><%= htDesc.toString() %></td></tr>
<% } %>
<p> <%= tables.length %> table(s) in set.</p>
</table>
<% } %>
<h2>Tables</h2>
<% ReturnMsg msg = show.execute(conf); %>
<p><%=msg %></p>
<h2>Region Servers</h2>
<% if (serverToServerInfos != null && serverToServerInfos.size() > 0) { %>
<% int totalRegions = 0;
int totalRequests = 0;
<% int totalRegions = 0;
int totalRequests = 0;
%>
<table>
<tr><th rowspan=<%= serverToServerInfos.size() + 1%>></th><th>Address</th><th>Start Code</th><th>Load</th></tr>
<% for (Map.Entry<String, HServerInfo> e: serverToServerInfos.entrySet()) {
HServerInfo hsi = e.getValue();
<% String[] serverNames = serverToServerInfos.keySet().toArray(new String[serverToServerInfos.size()]);
Arrays.sort(serverNames);
for (String serverName: serverNames) {
HServerInfo hsi = serverToServerInfos.get(serverName);
String url = "http://" +
hsi.getServerAddress().getBindAddress().toString() + ":" +
hsi.getServerAddress().getHostname().toString() + ":" +
hsi.getInfoPort() + "/";
String load = hsi.getLoad().toString();
String hostname = hsi.getServerAddress().getHostname() + ":" + hsi.getServerAddress().getPort();
totalRegions += hsi.getLoad().getNumberOfRegions();
totalRequests += hsi.getLoad().getNumberOfRequests();
totalRequests += hsi.getLoad().getNumberOfRequests() / interval;
long startCode = hsi.getStartCode();
String address = hsi.getServerAddress().toString();
%>
<tr><td><a href="<%= url %>"><%= address %></a></td><td><%= startCode %></td><td><%= load %></td></tr>
<tr><td><a href="<%= url %>"><%= hostname %></a></td><td><%= startCode %></td><td><%= hsi.getLoad().toString(interval) %></td></tr>
<% } %>
<tr><th>Total: </th><td>servers: <%= serverToServerInfos.size() %></td><td>&nbsp;</td><td>requests: <%= totalRequests %> regions: <%= totalRegions %></td></tr>
</table>
<p>Load is requests per <em>hbase.regionsserver.msginterval</em> (<%=interval%> second(s)) and count of regions loaded</p>
<p>Load is requests per second and count of regions loaded</p>
<% } %>
</body>
</html>

View File

@ -0,0 +1,72 @@
<%@ page contentType="text/html;charset=UTF-8"
import="org.apache.hadoop.io.Text"
import="org.apache.hadoop.hbase.HTableDescriptor"
import="org.apache.hadoop.hbase.client.HTable"
import="org.apache.hadoop.hbase.HRegionInfo"
import="org.apache.hadoop.hbase.HServerAddress"
import="org.apache.hadoop.hbase.HServerInfo"
import="org.apache.hadoop.hbase.master.HMaster"
import="org.apache.hadoop.hbase.master.MetaRegion"
import="java.io.IOException"
import="java.util.Map"
import="org.apache.hadoop.hbase.HConstants"%><%
HMaster master = (HMaster)getServletContext().getAttribute(HMaster.MASTER);
String tableName = request.getParameter("name");
HTable table = new HTable(master.getConfiguration(), new Text(tableName));
Map<String, HServerInfo> serverToServerInfos =
master.getServersToServerInfo();
String tableHeader = "<table><tr><th>Name</th><th>Region Server</th><th>Encoded Name</th><th>Start Key</th><th>End Key</th></tr>";
HServerAddress rootLocation = master.getRootRegionLocation();
%><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<title>Regions in <%= tableName %></title>
<link rel="stylesheet" type="text/css" href="/static/hbase.css" />
</head>
<body>
<a id="logo" href="http://wiki.apache.org/lucene-hadoop/Hbase"><img src="/static/hbase_logo_med.gif" alt="HBase Logo" title="HBase Logo" /></a>
<h1 id="page_title">Regions in <%= tableName %></h1>
<p id="links_menu"><a href="/master.jsp">Master</a>, <a href="/logs/">Local logs</a>, <a href="/stacks">Thread Dump</a>, <a href="/logLevel">Log Level</a></p>
<hr id="head_rule" />
<%if(tableName.equals(HConstants.ROOT_TABLE_NAME.toString())) {%>
<%= tableHeader %>
<% int infoPort = serverToServerInfos.get(rootLocation.getBindAddress()+":"+rootLocation.getPort()).getInfoPort();
String url = "http://" + rootLocation.getBindAddress() + ":" + infoPort + "/";%>
<tr><td><%= tableName %></td><td><a href="<%= url %>"><%= rootLocation.getHostname() %>:<%= rootLocation.getPort() %></a></td><td>-</td><td></td><td>-</td></tr>
</table>
<%} else if(tableName.equals(HConstants.META_TABLE_NAME.toString())) { %>
<%= tableHeader %>
<% Map<Text, MetaRegion> onlineRegions = master.getOnlineMetaRegions();
for (MetaRegion meta: onlineRegions.values()) {
int infoPort = serverToServerInfos.get(meta.getServer().getBindAddress()+":"+meta.getServer().getPort()).getInfoPort();
String url = "http://" + meta.getServer().getHostname() + ":" + infoPort + "/";%>
<tr><td><%= meta.getRegionName() %></td><td><a href="<%= url %>"><%= meta.getServer().getHostname() %>:<%= meta.getServer().getPort() %></a></td><td>-</td><td><%= meta.getStartKey() %></td><td>-</td></tr>
<% } %>
</table>
<%} else { %>
<%
try {
Map<HRegionInfo, HServerAddress> regions = table.getRegionsInfo();
if(regions != null && regions.size() > 0) { %>
<%= tableHeader %>
<% for(Map.Entry<HRegionInfo, HServerAddress> hriEntry : regions.entrySet()) { %>
<% System.out.println(serverToServerInfos.keySet().toArray()[0].toString());
System.out.println(hriEntry.getValue().getHostname()+":"+hriEntry.getValue().getPort());
int infoPort = serverToServerInfos.get(hriEntry.getValue().getHostname()+":"+hriEntry.getValue().getPort()).getInfoPort();
String url = "http://" + hriEntry.getValue().getHostname().toString() + ":" + infoPort + "/"; %>
<tr><td><%= hriEntry.getKey().getRegionName()%></td><td><a href="<%= url %>"><%= hriEntry.getValue().getHostname() %>:<%= hriEntry.getValue().getPort() %></a></td>
<td><%= hriEntry.getKey().getEncodedName()%></td> <td><%= hriEntry.getKey().getStartKey()%></td>
<td><%= hriEntry.getKey().getEndKey()%></td></tr>
<% } %>
</table>
<% }
}
catch(IOException ioe) { }
}%>
</body>
</html>

View File

@ -9,18 +9,19 @@
HRegionServer regionServer = (HRegionServer)getServletContext().getAttribute(HRegionServer.REGIONSERVER);
HServerInfo serverInfo = regionServer.getServerInfo();
Map<Text, HRegion> onlineRegions = regionServer.getOnlineRegions();
int interval = regionServer.getConfiguration().getInt("hbase.regionserver.msginterval", 3000)/1000;
%><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<title>HBase Region Server: <%= serverInfo.getServerAddress().toString() %></title>
<title>HBase Region Server: <%= serverInfo.getServerAddress().getHostname() %>:<%= serverInfo.getServerAddress().getPort() %></title>
<link rel="stylesheet" type="text/css" href="/static/hbase.css" />
</head>
<body>
<a id="logo" href="http://wiki.apache.org/lucene-hadoop/Hbase"><img src="/static/hbase_logo_med.gif" alt="HBase Logo" title="HBase Logo" /></a>
<h1 id="page_title">Region Server: <%= serverInfo.getServerAddress().toString() %></h1>
<h1 id="page_title">Region Server: <%= serverInfo.getServerAddress().getHostname() %>:<%= serverInfo.getServerAddress().getPort() %></h1>
<p id="links_menu"><a href="/logs/">Local logs</a>, <a href="/stacks">Thread Dump</a>, <a href="/logLevel">Log Level</a></p>
<hr id="head_rule" />
@ -29,15 +30,16 @@
<tr><th>Attribute Name</th><th>Value</th><th>Description</th></tr>
<tr><td>HBase Version</td><td><%= org.apache.hadoop.hbase.util.VersionInfo.getVersion() %>, r<%= org.apache.hadoop.hbase.util.VersionInfo.getRevision() %></td><td>HBase version and svn revision</td></tr>
<tr><td>HBase Compiled</td><td><%= org.apache.hadoop.hbase.util.VersionInfo.getDate() %>, <%= org.apache.hadoop.hbase.util.VersionInfo.getUser() %></td><td>When HBase version was compiled and by whom</td></tr>
<tr><td>Load</td><td><%= serverInfo.getLoad().toString() %></td><td>Requests/<em>hbase.regionserver.msginterval</em> + count of loaded regions</td></tr>
<tr><td>Load</td><td><%= serverInfo.getLoad().toString(interval) %></td><td>Requests per second and count of loaded regions</td></tr>
</table>
<h2>Online Regions</h2>
<% if (onlineRegions != null && onlineRegions.size() > 0) { %>
<table>
<tr><th>Region Name</th><th>Start Key</th><th>End Key</th></tr>
<tr><th>Region Name</th><th>Encoded Name</th><th>Start Key</th><th>End Key</th></tr>
<% for (HRegion r: onlineRegions.values()) { %>
<tr><td><%= r.getRegionName().toString() %></td><td><%= r.getStartKey().toString() %></td><td><%= r.getEndKey().toString() %></td></tr>
<tr><td><%= r.getRegionName().toString() %></td><td><%= r.getRegionInfo().getEncodedName() %></td>
<td><%= r.getStartKey().toString() %></td><td><%= r.getEndKey().toString() %></td></tr>
<% } %>
</table>
<p>Region names are made of the containing table's name, a comma,
@ -46,14 +48,12 @@ the region named
<em>domains,apache.org,5464829424211263407</em> is party to the table
<em>domains</em>, has an id of <em>5464829424211263407</em> and the first key
in the region is <em>apache.org</em>. The <em>-ROOT-</em>
and <em>.META.</em> 'tables' are internal sytem tables.
and <em>.META.</em> 'tables' are internal sytem tables (or 'catalog' tables in db-speak).
The -ROOT- keeps a list of all regions in the .META. table. The .META. table
keeps a list of all regions in the system. The empty key is used to denote
table start and table end. A region with an
empty start key is the first region in a table. If region has both an empty
start and an empty end key, its the only region in the table. See
<a href="http://wiki.apache.org/lucene-hadoop/Hbase">HBase Home</a> for
further explication.<p>
table start and table end. A region with an empty start key is the first region in a table.
If region has both an empty start and an empty end key, its the only region in the table. See
<a href="http://hbase.org">HBase Home</a> for further explication.<p>
<% } else { %>
<p>Not serving regions</p>
<% } %>