/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.scheduler.core.db.schedulerdb;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.objectweb.proactive.core.config.CentralPAPropertyRepository;
import org.objectweb.proactive.core.node.NodeFactory;
import org.ow2.proactive.authentication.crypto.Credentials;
import org.ow2.proactive.scheduler.common.SchedulerState;
import org.ow2.proactive.scheduler.common.job.JobId;
import org.ow2.proactive.scheduler.common.job.JobState;
import org.ow2.proactive.scheduler.common.job.JobStatus;
import org.ow2.proactive.scheduler.common.job.TaskFlowJob;
import org.ow2.proactive.scheduler.common.task.JavaTask;
import org.ow2.proactive.scheduler.common.task.Task;
import org.ow2.proactive.scheduler.common.task.TaskResult;
import org.ow2.proactive.scheduler.common.task.TaskState;
import org.ow2.proactive.scheduler.common.task.TaskStatus;
import org.ow2.proactive.scheduler.common.task.executable.JavaExecutable;
import org.ow2.proactive.scheduler.core.db.RecoveredSchedulerState;
import org.ow2.proactive.scheduler.core.db.SchedulerDBManager;
import org.ow2.proactive.scheduler.core.properties.PASchedulerProperties;
import org.ow2.proactive.scheduler.descriptor.EligibleTaskDescriptor;
import org.ow2.proactive.scheduler.job.InternalJob;
import org.ow2.proactive.scheduler.job.InternalJobFactory;
import org.ow2.proactive.scheduler.task.internal.ExecuterInformation;
import org.ow2.proactive.scheduler.task.internal.InternalTask;
import org.ow2.proactive.utils.ClasspathUtils;
import org.ow2.tests.ProActiveTest;
@Ignore
public class BaseSchedulerDBTest extends ProActiveTest {
protected SchedulerDBManager dbManager;
private static Credentials defaultCredentials;
protected static final String DEFAULT_USER_NAME = "admin";
protected boolean inMemory = false;
public BaseSchedulerDBTest() {
this(false);
}
public BaseSchedulerDBTest(boolean inMemory) {
this.inMemory = inMemory;
}
public static class TestResult implements Serializable {
private int a;
private String b;
TestResult(int a, String b) {
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public String getB() {
return b;
}
}
public static class TestException extends Exception {
private String data;
public TestException(String message, String data) {
super(message);
this.data = data;
}
public String getData() {
return data;
}
}
static class StateMatcher extends TypeSafeMatcher<SchedulerState> {
private final List<JobStateMatcher> pending = new ArrayList<>();
private final List<JobStateMatcher> running = new ArrayList<>();
private final List<JobStateMatcher> finished = new ArrayList<>();
@Override
public void describeTo(Description description) {
description.appendText("State with jobs");
for (JobStateMatcher job : pending) {
description.appendDescriptionOf(job);
}
for (JobStateMatcher job : running) {
description.appendDescriptionOf(job);
}
for (JobStateMatcher job : finished) {
description.appendDescriptionOf(job);
}
}
StateMatcher withPending(JobStateMatcher job) {
pending.add(job);
return this;
}
StateMatcher withRunning(JobStateMatcher job) {
running.add(job);
return this;
}
StateMatcher withFinished(JobStateMatcher job) {
finished.add(job);
return this;
}
@Override
protected boolean matchesSafely(SchedulerState item) {
assertThat(item.getPendingJobs(), containsInAnyOrder(pending.toArray(new JobStateMatcher[] {})));
assertThat(item.getRunningJobs(), containsInAnyOrder(running.toArray(new JobStateMatcher[] {})));
assertThat(item.getFinishedJobs(), containsInAnyOrder(finished.toArray(new JobStateMatcher[] {})));
return true;
}
}
public SchedulerDBManager getDbManager() {
return dbManager;
}
public static Credentials getDefaultCredentials() throws Exception {
if (defaultCredentials == null) {
defaultCredentials = Credentials.createCredentials(DEFAULT_USER_NAME,
"admin",
new File(BaseSchedulerDBTest.class.getResource("/functionaltests/config/pub.key")
.toURI()).getAbsolutePath());
}
return defaultCredentials;
}
protected RecoveredSchedulerState checkRecoveredState(RecoveredSchedulerState state, StateMatcher stateMatcher) {
assertThat(state.getSchedulerState(), stateMatcher);
assertThat(state.getPendingJobs(), containsInAnyOrder(stateMatcher.pending.toArray(new JobStateMatcher[] {})));
assertThat(state.getRunningJobs(), containsInAnyOrder(stateMatcher.running.toArray(new JobStateMatcher[] {})));
assertThat(state.getFinishedJobs(),
containsInAnyOrder(stateMatcher.finished.toArray(new JobStateMatcher[] {})));
return state;
}
static Set<JobStatus> finishedJobStatus = new HashSet<>();
static {
finishedJobStatus.add(JobStatus.CANCELED);
finishedJobStatus.add(JobStatus.FAILED);
finishedJobStatus.add(JobStatus.KILLED);
finishedJobStatus.add(JobStatus.FINISHED);
}
static class JobStateMatcher extends TypeSafeMatcher<JobState> {
private final JobId id;
private final List<TaskStateMatcher> pending = new ArrayList<>();
private final List<TaskStateMatcher> running = new ArrayList<>();
private final List<TaskStateMatcher> finished = new ArrayList<>();
private final Set<String> eligibleTasks = new HashSet<>();
private final JobStatus status;
private int finishedNumber;
private int pendingNumber;
private boolean checkFinished;
static JobStateMatcher job(JobId id, JobStatus status) {
return new JobStateMatcher(id, status);
}
public JobStateMatcher(JobId id, JobStatus status) {
this.id = id;
this.status = status;
}
JobStateMatcher withEligible(String... tasks) {
eligibleTasks.addAll(Arrays.asList(tasks));
return this;
}
JobStateMatcher withPending(TaskStateMatcher task, boolean addToCount) {
pending.add(task);
if (addToCount) {
pendingNumber++;
}
return this;
}
JobStateMatcher withRunning(TaskStateMatcher task) {
running.add(task);
return this;
}
JobStateMatcher withFinished(TaskStateMatcher task) {
return withFinished(task, true);
}
JobStateMatcher withFinished(TaskStateMatcher task, boolean addToCount) {
finished.add(task);
if (addToCount) {
finishedNumber++;
}
return this;
}
JobStateMatcher checkFinished() {
checkFinished = true;
return this;
}
@Override
public void describeTo(Description description) {
description.appendText("Job " + id);
}
@Override
protected boolean matchesSafely(JobState item) {
if (!id.equals(item.getId())) {
return false;
}
Assert.assertEquals(status, item.getStatus());
Assert.assertEquals("Pending tasks for " + id, pendingNumber, item.getNumberOfPendingTasks());
Assert.assertEquals("Running tasks for " + id, running.size(), item.getNumberOfRunningTasks());
Assert.assertEquals("Finished tasks for " + id, finishedNumber, item.getNumberOfFinishedTasks());
Collection<TaskStateMatcher> all = new ArrayList<>(pending.size() + running.size() + finished.size());
all.addAll(pending);
all.addAll(running);
all.addAll(finished);
assertThat("Tasks for " + id, item.getTasks(), containsInAnyOrder(all.toArray(new TaskStateMatcher[] {})));
assertThat("Submitted time for " + id, item.getSubmittedTime(), greaterThan(0L));
if (checkFinished) {
assertThat("Started time for " + id, item.getStartTime(), greaterThan(0L));
assertThat("Finished time for " + id, item.getFinishedTime(), greaterThan(0L));
}
if (item instanceof InternalJob) {
InternalJob internalJob = (InternalJob) item;
if (!finishedJobStatus.contains(status)) {
Set<String> actualEligible = new HashSet<>();
for (EligibleTaskDescriptor desc : internalJob.getJobDescriptor().getEligibleTasks()) {
actualEligible.add(desc.getTaskId().getReadableName());
}
Assert.assertEquals("Eligible tasks for " + id, eligibleTasks, actualEligible);
}
}
return true;
}
}
static class TaskStateMatcher extends TypeSafeMatcher<TaskState> {
private final String name;
private final TaskStatus status;
private boolean checkFinished;
public TaskStateMatcher(String name, TaskStatus status) {
this.name = name;
this.status = status;
}
public TaskStateMatcher checkFinished() {
checkFinished = true;
return this;
}
@Override
public void describeTo(Description desc) {
desc.appendText("Task " + name + ", " + status);
}
@Override
public boolean matchesSafely(TaskState item) {
if (!item.getName().equals(name)) {
return false;
}
Assert.assertEquals("Status for " + item.getName(), status, item.getStatus());
if (checkFinished) {
assertThat("Finished time for " + item.getName(), item.getFinishedTime(), greaterThan(0L));
} else {
assertThat("Finished time for " + item.getName(), item.getFinishedTime(), is(-1L));
}
return true;
}
boolean checkIfTheSame(TaskState task) {
if (!name.equals(task.getName())) {
return false;
}
Assert.assertEquals(status, task.getStatus());
return true;
}
}
static StateMatcher state() {
return new StateMatcher();
}
static TaskStateMatcher task(String name, TaskStatus status) {
return new TaskStateMatcher(name, status);
}
static JobStateMatcher job(JobId id, JobStatus status) {
return new JobStateMatcher(id, status);
}
@Before
public void initTest() throws Exception {
PASchedulerProperties.SCHEDULER_HOME.updateProperty(ClasspathUtils.findSchedulerHome());
PASchedulerProperties.TASK_FORK.updateProperty("true");
CentralPAPropertyRepository.PA_CLASSLOADING_USEHTTP.setValue(false);
if (inMemory) {
dbManager = SchedulerDBManager.createInMemorySchedulerDBManager();
} else {
Configuration config = new Configuration().configure(new File(this.getClass()
.getResource("/functionaltests/config/hibernate.cfg.xml")
.toURI()));
dbManager = new SchedulerDBManager(config, true);
}
}
@After
public void cleanup() {
if (dbManager != null) {
dbManager.close();
}
}
public static class TestDummyExecutable extends JavaExecutable {
@Override
public Serializable execute(TaskResult... results) throws Throwable {
return null;
}
}
public InternalJob defaultSubmitJob(TaskFlowJob job) throws Exception {
return defaultSubmitJob(job, DEFAULT_USER_NAME, -1);
}
public InternalJob defaultSubmitJob(TaskFlowJob job, String userName) throws Exception {
return defaultSubmitJob(job, userName, -1);
}
public InternalJob defaultSubmitJob(TaskFlowJob job, String userName, long submittedTime) throws Exception {
if (job.getTasks().isEmpty()) {
job.addTask(createDefaultTask("default test task"));
}
InternalJob internalJob = InternalJobFactory.createJob(job, getDefaultCredentials());
internalJob.setOwner(userName);
internalJob.submitAction();
if (submittedTime > 0) {
internalJob.setSubmittedTime(submittedTime);
}
dbManager.newJobSubmitted(internalJob);
return internalJob;
}
public InternalJob loadInternalJob(boolean fullState, JobId jobId) {
List<InternalJob> jobs = dbManager.loadJobs(fullState, jobId);
Assert.assertEquals(1, jobs.size());
return jobs.get(0);
}
public InternalJob defaultSubmitJobAndLoadInternal(boolean fullState, TaskFlowJob jobDef) throws Exception {
return defaultSubmitJobAndLoadInternal(fullState, jobDef, DEFAULT_USER_NAME);
}
public InternalJob defaultSubmitJobAndLoadInternal(boolean fullState, TaskFlowJob jobDef, String userName)
throws Exception {
InternalJob job = defaultSubmitJob(jobDef, userName);
return loadInternalJob(fullState, job.getId());
}
protected static JavaTask createDefaultTask(String taskName) {
JavaTask task = new JavaTask();
task.setName(taskName);
task.setExecutableClassName(TestDummyExecutable.class.getName());
return task;
}
public InternalJob saveSingleTask(Task task) throws Exception {
TaskFlowJob job = new TaskFlowJob();
job.addTask(task);
InternalJob jobData = defaultSubmitJobAndLoadInternal(true, job);
Assert.assertEquals(1, jobData.getTasks().size());
return jobData;
}
protected TaskState findTask(JobState jobState, String taskName) {
for (TaskState taskState : jobState.getTasks()) {
if (taskState.getName().equals(taskName)) {
return taskState;
}
}
Assert.fail("Didn't find task with name " + taskName);
return null;
}
protected InternalTask startTask(InternalJob internalJob, InternalTask internalTask) throws Exception {
internalTask.setExecuterInformation(new ExecuterInformation(null, NodeFactory.getDefaultNode()));
internalJob.startTask(internalTask);
return internalTask;
}
protected String createString(int length) {
StringBuilder string = new StringBuilder(length);
for (int i = 0; i < length; i++) {
string.append("a");
}
return string.toString();
}
}