package org.opennms.netmgt.ncs.rest;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.ws.rs.core.MediaType;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.opennms.core.test.MockLogAppender;
import org.opennms.core.utils.LogUtils;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.mock.EventAnticipator;
import org.opennms.netmgt.mock.MockEventIpcManager;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.ncs.NCSComponent;
import org.opennms.netmgt.model.ncs.NCSComponentRepository;
import org.opennms.netmgt.ncs.persistence.NCSComponentDao;
import org.opennms.netmgt.ncs.persistence.NCSComponentService;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.springframework.mock.web.MockHttpServletRequest;
public class NCSRestServiceTest extends AbstractSpringJerseyRestTestCase {
private static void setupLogging(final String level) {
final Properties config = new Properties();
config.setProperty("log4j.logger.org.opennms.netmgt.mock.MockEventIpcManager", "ERROR");
config.setProperty("log4j.logger.org.springframework", "ERROR");
config.setProperty("log4j.logger.org.hibernate", "ERROR");
MockLogAppender.setupLogging(true, level, config);
}
@BeforeClass
public static void setupLogging() {
setupLogging("ERROR");
}
private static final String m_serviceXML = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"Service\" foreignId=\"123\" foreignSource=\"NA-Service\">\n" +
" <name>CokeP2P</name>\n" +
" <component type=\"ServiceElement\" foreignId=\"8765,1234\" foreignSource=\"NA-ServiceElement\">\n" +
" <name>PE1,ge-1/0/2</name>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"8765,ge-1/0/2.50\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>ge-1/0/2.50</name>\n" +
" <component type=\"PhysicalInterface\" foreignId=\"8765,ifIndex-1\" foreignSource=\"NA-PhysIfs\">\n" +
" <name>ge-1/0/2</name>\n" +
" </component>\n" +
" </component>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"8765,vcid(50)\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>PE1,vcid(50)</name>\n" +
" <dependenciesRequired>ANY</dependenciesRequired>\n" +
" <attributes>\n" +
" <attribute>\n" +
" <key>jnxVpnPwVpnType</key>\n" +
" <value>5</value>\n" +
" </attribute>\n" +
" <attribute>\n" +
" <key>jnxVpnPwVpnName</key>\n" +
" <value>ge-1/0/2.2</value>\n" +
" </attribute>\n" +
" </attributes>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"8765,LSP-1234\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>lspA-PE1-PE2</name>\n" +
" </component>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"8765,LSP-4321\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>lspB-PE1-PE2</name>\n" +
" </component>\n" +
" </component>\n" +
" </component>\n" +
" <component type=\"ServiceElement\" foreignId=\"9876,4321\" foreignSource=\"NA-ServiceElement\">\n" +
" <name>PE2,ge-3/1/4</name>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"9876,ge-3/1/4.50\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>ge-3/1/4.50</name>\n" +
" <component type=\"PhysicalInterface\" foreignId=\"9876,ifIndex-3\" foreignSource=\"NA-PhysIfs\">\n" +
" <name>ge-3/1/4</name>\n" +
" </component>\n" +
" </component>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"9876,vcid(50)\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>PE2,vcid(50)</name>\n" +
" <dependenciesRequired>ANY</dependenciesRequired>\n" +
" <attributes>\n" +
" <attribute>\n" +
" <key>jnxVpnPwVpnType</key>\n" +
" <value>5</value>\n" +
" </attribute>\n" +
" <attribute>\n" +
" <key>jnxVpnPwVpnName</key>\n" +
" <value>ge-3/1/4.2</value>\n" +
" </attribute>\n" +
" </attributes>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"9876,LSP-1234\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>lspA-PE2-PE1</name>\n" +
" </component>\n" +
" <component type=\"ServiceElementComponent\" foreignId=\"9876,LSP-4321\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>lspB-PE2-PE1</name>\n" +
" </component>\n" +
" </component>\n" +
" </component>\n" +
"</component>\n";
private static final String[][] m_components = new String[][] {
new String[] { "Service", "CokeP2P", "NA-Service", "123" },
new String[] { "ServiceElement", "PE1,ge-1/0/2", "NA-ServiceElement", "8765,1234" },
new String[] { "ServiceElementComponent", "ge-1/0/2.50", "NA-SvcElemComp", "8765,ge-1/0/2.50" },
new String[] { "PhysicalInterface", "ge-1/0/2", "NA-PhysIfs", "8765,ifIndex-1" },
new String[] { "ServiceElementComponent", "PE1,vcid(50)", "NA-SvcElemComp", "8765,vcid(50)" },
new String[] { "ServiceElementComponent", "lspA-PE1-PE2", "NA-SvcElemComp", "8765,LSP-1234" },
new String[] { "ServiceElementComponent", "lspB-PE1-PE2", "NA-SvcElemComp", "8765,LSP-4321" },
new String[] { "ServiceElement", "PE2,ge-3/1/4", "NA-ServiceElement", "9876,4321" },
new String[] { "ServiceElementComponent", "ge-3/1/4.50", "NA-SvcElemComp", "9876,ge-3/1/4.50" },
new String[] { "PhysicalInterface", "ge-3/1/4", "NA-PhysIfs", "9876,ifIndex-3" },
new String[] { "ServiceElementComponent", "PE2,vcid(50)", "NA-SvcElemComp", "9876,vcid(50)" },
new String[] { "ServiceElementComponent", "lspA-PE2-PE1", "NA-SvcElemComp", "9876,LSP-1234" },
new String[] { "ServiceElementComponent", "lspB-PE2-PE1", "NA-SvcElemComp", "9876,LSP-4321" } };
private static final String m_serviceXMLFragment = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"ServiceElementComponent\" foreignId=\"9876,vcid(50)\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>PE2,vcid(50)</name>\n" +
" <dependenciesRequired>ANY</dependenciesRequired>\n" +
"</component>\n";
private static final String m_serviceXMLTopFragment = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"Service\" foreignId=\"123\" foreignSource=\"NA-Service\">\n" +
" <name>CokeP2P</name>\n" +
"</component>\n";
private static final String m_extraXML = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"ServiceElementComponent\" foreignId=\"monkey1\" foreignSource=\"NA-SvcElemComp\">\n" +
" <name>Monkey (1)</name>\n" +
" <component type=\"PhysicalInterface\" foreignId=\"shoe2\" foreignSource=\"NA-PhysIfs\">\n" +
" <name>Shoe (2)</name>\n" +
" </component>\n" +
"</component>\n";
private static final String m_badForeignSourceXML = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"Service\" foreignId=\"123\" foreignSource=\"NA-Service:1\">\n" +
" <name>Blah</name>\n" +
"</component>\n";
private static final String m_badForeignIdXML = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"Service\" foreignId=\"123:456\" foreignSource=\"NA-Service\">\n" +
" <name>Blah</name>\n" +
"</component>\n";
private MockEventIpcManager m_eventIpcManager;
private EventAnticipator m_eventAnticipator;
@Override
protected void afterServletStart() throws Exception {
m_eventIpcManager = getWebAppContext().getBean(MockEventIpcManager.class);
m_eventAnticipator = m_eventIpcManager.getEventAnticipator();
final NCSComponentService service = getWebAppContext().getBean(NCSComponentService.class);
service.setEventProxy(m_eventIpcManager);
}
@After
@Override
public void tearDown() throws Exception {
final Collection<Event> events = m_eventAnticipator.unanticipatedEvents();
for (final Event e : events) {
System.err.println("unanticipated event: " + e.getUei() + formatParms(e.getParmCollection()));
}
setupLogging("ERROR");
m_eventAnticipator.verifyAnticipated();
m_eventAnticipator.reset();
final NCSComponentDao dao = getWebAppContext().getBean(NCSComponentDao.class);
dao.flush();
super.tearDown();
}
@Test
public void testPostAService() throws Exception {
setupLogging("DEBUG");
anticipateEvents(EventConstants.COMPONENT_ADDED_UEI);
sendPost("/NCS", m_serviceXML);
final NCSComponentRepository repo = getBean("ncsComponentRepository", NCSComponentRepository.class);
for (final NCSComponent component : repo.findAll()) {
LogUtils.debugf(this, "Found Component: %s/%s/%s", component.getType(), component.getForeignSource(), component.getForeignId());
}
String url = "/NCS/ServiceElementComponent/NA-SvcElemComp:9876%2Cvcid(50)";
// Testing GET Collection
System.err.println("GET!!!");
String xml = sendRequest(GET, url, 200);
assertTrue(xml.contains("jnxVpnPwVpnName"));
}
@Test
public void testDeleteAComponent() throws Exception {
sendPost("/NCS", m_serviceXML);
m_eventAnticipator.reset();
anticipateEvent(EventConstants.COMPONENT_DELETED_UEI, new String[] { "ServiceElementComponent", "PE2,vcid(50)", "NA-SvcElemComp", "9876,vcid(50)" });
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "ServiceElement", "PE2,ge-3/1/4", "NA-ServiceElement", "9876,4321" });
setupLogging("DEBUG");
String url = "/NCS/ServiceElementComponent/NA-SvcElemComp:9876%2Cvcid(50)";
// Testing GET Collection
String xml = sendRequest(GET, url, 200);
assertTrue(xml.contains("jnxVpnPwVpnName"));
sendRequest(DELETE, url, 200);
sendRequest(GET, url, 400);
sendRequest(GET, "/NCS/Service/NA-Service:123", 200);
}
@Test
public void testGetANonExistingService() throws Exception {
setupLogging("DEBUG");
// This service should not exist
String url = "/NCS/Service/hello:world";
// Testing GET Collection
sendRequest(GET, url, 400);
}
@Test
public void testFindAServiceByAttribute() throws Exception {
sendPost("/NCS", m_serviceXML);
m_eventAnticipator.reset();
setupLogging("DEBUG");
String url = "/NCS/attributes";
// Testing GET Collection
String xml = sendRequest(GET, url, 200);
assertTrue(xml.contains("jnxVpnPwVpnName"));
}
@Test
public void testAddComponents() throws Exception {
sendPost("/NCS", m_serviceXML);
m_eventAnticipator.reset();
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "ServiceElement", "PE2,ge-3/1/4", "NA-ServiceElement", "9876,4321" });
anticipateEvent(EventConstants.COMPONENT_ADDED_UEI, new String[] { "ServiceElementComponent", "Monkey (1)", "NA-SvcElemComp", "monkey1" });
anticipateEvent(EventConstants.COMPONENT_ADDED_UEI, new String[] { "PhysicalInterface", "Shoe (2)", "NA-PhysIfs", "shoe2" });
setupLogging("DEBUG");
String url = "/NCS/ServiceElement/NA-ServiceElement:9876,4321";
sendPost(url, m_extraXML);
String xml = sendRequest(GET, url, 200);
assertTrue(xml.contains("monkey1"));
}
@Test
public void testDeleteOrphans() throws Exception {
sendPost("/NCS", m_serviceXML);
m_eventAnticipator.reset();
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "ServiceElementComponent", "PE2,vcid(50)", "NA-SvcElementComp", "9876,vcid(50)" });
anticipateEvent(EventConstants.COMPONENT_DELETED_UEI, new String[] { "ServiceElementComponent", "lspA-PE2-PE1", "NA-SvcElemComp", "9876,LSP-1234" });
anticipateEvent(EventConstants.COMPONENT_DELETED_UEI, new String[] { "ServiceElementComponent", "lspB-PE2-PE1", "NA-SvcElemComp", "9876,LSP-4321" });
setupLogging("DEBUG");
final MockHttpServletRequest request = createRequest(POST, "/NCS");
request.setContentType(MediaType.APPLICATION_XML);
request.setContent(m_serviceXMLFragment.getBytes());
request.setQueryString("deleteOrphans=true");
sendRequest(request, 200);
}
/*
* Deletes everything but the top-level "Service" component.
*/
@Test
public void testDeleteOrphansRecursive() throws Exception {
sendPost("/NCS", m_serviceXML);
m_eventAnticipator.reset();
setupLogging("DEBUG");
// skip the 1st, since it will be "updated" instead of "deleted"
for (int i = 1; i < m_components.length; i++) {
anticipateEvent(EventConstants.COMPONENT_DELETED_UEI, m_components[i]);
}
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "Service", "CokeP2P", "NA-Service", "123" });
final MockHttpServletRequest request = createRequest(POST, "/NCS");
request.setContentType(MediaType.APPLICATION_XML);
request.setContent(m_serviceXMLTopFragment.getBytes());
request.setQueryString("deleteOrphans=true");
sendRequest(request, 200);
}
@Test
public void testMultiParent() throws Exception {
createMultiParent();
final String xml = sendRequest(GET, "/NCS/top/topFs1:topFd1", 200);
assertTrue(xml.contains("topFs1"));
assertTrue(xml.contains("topFd1"));
assertTrue(xml.contains("child1Fs1"));
assertTrue(xml.contains("child1Fd1"));
assertTrue(xml.contains("child1Fd2"));
assertTrue(xml.contains("child2Fs1"));
assertTrue(xml.contains("child2Fd1"));
}
@Test
public void testDeleteMultiParentOrphans() throws Exception {
setupLogging("DEBUG");
createMultiParent();
/*
* we should now have a tree of:
*
* Top1
* Child1-1
* Child2-1+
* Child1-2
* Child2-1+
*
* + should be the same object
*/
m_eventAnticipator.verifyAnticipated();
m_eventAnticipator.reset();
setupLogging("DEBUG");
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "top", "Top1", "topFs1", "topFd1" });
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "child1", "Child1-2", "child1Fs1", "child1Fd2" });
anticipateEvent(EventConstants.COMPONENT_DELETED_UEI, new String[] { "child1", "Child1-1", "child1Fs1", "child1Fd1" });
sendRequest(DELETE, "/NCS/child1/child1Fs1:child1Fd1", parseParamData("deleteOrphans=true"), 200);
String xml = sendRequest(GET, "/NCS/top/topFs1:topFd1", 200);
assertFalse(xml.contains("child1Fd1"));
assertTrue(xml.contains("child2Fd1"));
}
private void createMultiParent() throws Exception {
// create a simple 3-level tree
String text = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"top\" foreignSource=\"topFs1\" foreignId=\"topFd1\">\n" +
" <name>Top1</name>\n" +
" <component type=\"child1\" foreignSource=\"child1Fs1\" foreignId=\"child1Fd1\">\n" +
" <name>Child1-1</name>\n" +
" <component type=\"child2\" foreignSource=\"child2Fs1\" foreignId=\"child2Fd1\">\n" +
" <name>Child2-1</name>\n" +
" </component>\n" +
" </component>\n" +
"</component>\n";
anticipateEvent(EventConstants.COMPONENT_ADDED_UEI, new String[] { "top", "Top1", "topFs1", "topFd1" });
anticipateEvent(EventConstants.COMPONENT_ADDED_UEI, new String[] { "child1", "Child1-1", "child1Fs1", "child1Fd1" });
anticipateEvent(EventConstants.COMPONENT_ADDED_UEI, new String[] { "child2", "Child2-1", "child2Fs1", "child2Fd1" });
sendPost("/NCS", text, 200);
// create another "child1" type with the same "child2" type under it
text = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
// note the foreignId is different
" <component xmlns=\"http://xmlns.opennms.org/xsd/model/ncs\" type=\"child1\" foreignSource=\"child1Fs1\" foreignId=\"child1Fd2\">\n" +
" <name>Child1-2</name>\n" +
// but the child2 component is the same
" <component type=\"child2\" foreignSource=\"child2Fs1\" foreignId=\"child2Fd1\">\n" +
" <name>Child2-1</name>\n" +
" </component>\n" +
" </component>\n";
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "top", "Top1", "topFs1", "topFd1" });
anticipateEvent(EventConstants.COMPONENT_ADDED_UEI, new String[] { "child1", "Child1-2", "child1Fs1", "child1Fd2" });
anticipateEvent(EventConstants.COMPONENT_UPDATED_UEI, new String[] { "child2", "Child2-1", "child2Fs1", "child2Fd1" });
sendPost("/NCS/top/topFs1:topFd1", text, 200);
}
private String formatParms(final List<Parm> parms) {
final StringBuilder sb = new StringBuilder();
sb.append("[");
if (parms.size() > 0) {
final Iterator<Parm> parmIterator = parms.iterator();
while (parmIterator.hasNext()) {
final Parm parm = parmIterator.next();
sb.append("'").append(parm.getParmName()).append("'='");
sb.append(parm.getValue().getContent()).append("'");
if (parmIterator.hasNext()) sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
@Test
@Ignore("allowing this for now")
public void testInvalidForeignSource() throws Exception {
sendPost("/NCS", m_badForeignSourceXML, 400);
}
@Test
@Ignore("allowing this for now")
public void testInvalidForeignId() throws Exception {
sendPost("/NCS", m_badForeignIdXML, 400);
}
private void anticipateEvents(final String uei) {
for (final String[] componentInfo : m_components) {
anticipateEvent(uei, componentInfo);
}
}
private void anticipateEvent(final String uei, final String[] componentInfo) {
final EventBuilder builder = new EventBuilder(uei, "NCSComponentService");
builder.addParam("componentType", componentInfo[0]);
builder.addParam("componentName", componentInfo[1]);
builder.addParam("componentForeignSource", componentInfo[2]);
builder.addParam("componentForeignId", componentInfo[3]);
m_eventAnticipator.anticipateEvent(builder.getEvent());
}
}