Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Billing adapter checks 2 #7450

Merged
merged 10 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ public boolean equals(final Object other) {
}
ChannelFamily castOther = (ChannelFamily) other;

return new EqualsBuilder().append(id, castOther.id)
.append(label, castOther.label)
.append(name, castOther.name)
.append(org, castOther.org)
return new EqualsBuilder().append(id, castOther.getId())
.append(label, castOther.getLabel())
.append(name, castOther.getName())
.append(org, castOther.getOrg())
.isEquals();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ public ActionForward execute(ActionMapping mapping,
public List<Map<String, Object>> getResult(RequestContext contextIn) {
User user = contextIn.getCurrentUser();
try {
return new TaskomaticApi().findActiveSchedules(user);
List<Map<String, Object>> activeSchedules = new TaskomaticApi().findActiveSchedules(user);
activeSchedules.removeIf(s -> s.get("job_label").equals("payg-dimension-computation-default"));
return activeSchedules;
}
catch (TaskomaticApiException e) {
createErrorMessage(contextIn.getRequest(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.redhat.rhn.frontend.struts.RequestContext;
import com.redhat.rhn.frontend.struts.RhnAction;
import com.redhat.rhn.frontend.struts.RhnHelper;
import com.redhat.rhn.taskomatic.TaskoFactory;
import com.redhat.rhn.taskomatic.TaskomaticApi;
import com.redhat.rhn.taskomatic.TaskomaticApiException;

Expand Down Expand Up @@ -187,6 +186,10 @@ private void setupForm(HttpServletRequest request, DynaActionForm form) {
TaskomaticApi tapi = new TaskomaticApi();
Map<String, Object> schedule = tapi.lookupScheduleById(loggedInUser, schid);
String scheduleName = (String) schedule.get("job_label");
if (scheduleName.equals("payg-dimension-computation-default")) {
// not modifiable
return;
}
String bunchName = (String) schedule.get("bunch");
request.setAttribute("schedulename", scheduleName);
form.set("schedulename", scheduleName);
Expand All @@ -212,7 +215,9 @@ private void prepDropdowns(RequestContext ctx) {
List<Map<String, String>> dropDown = new ArrayList<>();
try {
List<Map<String, Object>> bunches = new TaskomaticApi().listSatBunchSchedules(loggedInUser);
bunches.removeIf(bunch -> TaskoFactory.HIDDEN_BUNCHES.contains(bunch.get("name")));
// Since recurring states have their own place in the webUI we don't
// want them to show up in the Task Schedules UI
bunches.removeIf(bunch -> bunch.get("name").equals("recurring-action-executor-bunch"));

for (Map<String, Object> b : bunches) {
addOption(dropDown, (String)b.get("name"), (String)b.get("name"));
Expand Down
12 changes: 3 additions & 9 deletions java/code/src/com/redhat/rhn/taskomatic/TaskoFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ public class TaskoFactory extends HibernateFactory {
private static TaskoFactory singleton = new TaskoFactory();
private static Logger log = LogManager.getLogger(TaskoFactory.class);

// Since recurring states have their own place in the webUI we don't
// want them to show up in the Task Schedules UI and dimension computation
// should also run always without changes from the admin
public static final List<String> HIDDEN_BUNCHES =
List.of("recurring-action-executor-bunch", "payg-dimension-computation-bunch");

/**
* default constructor
*/
Expand Down Expand Up @@ -216,6 +210,7 @@ public static void deleteRun(TaskoRun run) {
*/
public static List<TaskoSchedule> listActiveSchedulesByOrg(Integer orgId) {
List<TaskoSchedule> schedules;
List<String> filter = List.of("recurring-action-executor-bunch"); // List of bunch names to be excluded
Map<String, Object> params = new HashMap<>();

params.put("timestamp", new Date()); // use server time, not DB time
Expand All @@ -227,8 +222,8 @@ public static List<TaskoSchedule> listActiveSchedulesByOrg(Integer orgId) {
schedules = singleton.listObjectsByNamedQuery("TaskoSchedule.listActiveByOrg", params);
}

// Remove hidden schedules with blacklisted bunch names
schedules.removeIf(schedule -> HIDDEN_BUNCHES.contains(schedule.getBunch().getName()));
// Remove schedules with bunch names in 'filter'
schedules.removeIf(schedule -> filter.contains(schedule.getBunch().getName()));
return schedules;
}

Expand All @@ -245,7 +240,6 @@ public static List<TaskoSchedule> listActiveSchedulesByOrgAndLabel(Integer orgId
params.put("timestamp", new Date()); // use server time, not DB time
if (orgId == null) {
return singleton.listObjectsByNamedQuery("TaskoSchedule.listActiveInSatByLabel", params);

}
params.put("org_id", orgId);
return singleton.listObjectsByNamedQuery("TaskoSchedule.listActiveByOrgAndLabel", params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public String getConfigNamespace() {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
if (!cloudManager.isPaygInstance()) {
LOGGER.debug("Not a PAYG instance. Exit");
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ def _get_installed_suse_products():
"version": product_xml.find("./version").text,
"arch": product_xml.find("./arch").text
}
if product["name"] == "sle-manager-tools":
# no payg product has manager tools. When it appears, it comes from
# a registration against SUSE Manager
continue
products.append(product)
return products

Expand Down
68 changes: 57 additions & 11 deletions java/code/src/com/suse/cloud/CloudPaygManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,8 @@ private boolean detectIsCompliant() {
}

// files of this package should not be modified
String[] cmd = {"/usr/bin/rpm", "-V", "billing-data-service"};
SystemCommandExecutor scexec = new SystemCommandExecutor();
int retcode = scexec.execute(cmd);
if (retcode != 0) {
// 5 means checksum changed / file is modified. Example "S.5....T. /path/to/file"
if (scexec.getLastCommandOutput().lines().anyMatch(l -> l.charAt(2) == '5')) {
LOG.error("Billing Data Service has modifications");
LOG.info(scexec.getLastCommandOutput());
return false;
}
if (hasPackageModifications("billing-data-service")) {
return false;
}

// we only need to check compliance for SUMA PAYG
Expand All @@ -272,10 +264,64 @@ private boolean detectIsCompliant() {
LOG.info(e.getMessage(), e);
return false;
}
//TODO: Check billing adapter report

// files of this package should not be modified
if (hasPackageModifications("csp-billing-adapter-service") ||
hasPackageModifications("python3-csp-billing-adapter") ||
hasPackageModifications("python3-csp-billing-adapter-local")) {
return false;
}
if (cloudProvider.equals(CloudProvider.AWS) &&
hasPackageModifications("python3-csp-billing-adapter-amazon")) {
return false;
}
if (cloudProvider.equals(CloudProvider.AZURE) &&
hasPackageModifications("python3-csp-billing-adapter-azure")) {
return false;
}
return isServiceRunning("csp-billing-adapter.service");
}

/**
* Test if the provided service is running
* @param serviceIn service name
* @return true when it is running, otherwise false
*/
protected boolean isServiceRunning(String serviceIn) {
String[] cmd = {"/usr/bin/systemctl", "-q", "is-active", serviceIn};
SystemCommandExecutor scexec = new SystemCommandExecutor();
int retcode = scexec.execute(cmd);
if (retcode != 0) {
LOG.error("Service '{}' is not running.", serviceIn);
return false;
}
return true;
}

/**
* Test if the files of the given package name have modifications.
* It checks only the checksum of the files. Modifications of the permissions
* or ownership are not detected.
* Also if the package is not installed does not result in an error.
* @param pkg the package name to check
* @return true if the package is installed and files were modified, otherwise false
*/
protected boolean hasPackageModifications(String pkg) {
String[] cmd = {"/usr/bin/rpm", "-V", pkg};
SystemCommandExecutor scexec = new SystemCommandExecutor();
int retcode = scexec.execute(cmd);
if (retcode != 0) {
// missing packages result in message "package ... is not installed" and will not match "5"
// 5 means checksum changed / file is modified. Example "S.5....T. /path/to/file"
if (scexec.getLastCommandOutput().lines().anyMatch(l -> l.charAt(2) == '5')) {
LOG.error("Package '{}' was modifified", pkg);
LOG.info(scexec.getLastCommandOutput());
return true;
}
}
return false;
}

/**
* Check if file exists and is executable
* @param filename a filename to check
Expand Down
139 changes: 79 additions & 60 deletions java/code/src/com/suse/cloud/test/CloudPaygManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,59 @@ else if (exception.isPresent()) {
}
}

public static class CloudPaygManagerTestHelper extends CloudPaygManager {

private String iType = "BYOS";
private String reqResult = "";
private boolean serviceRunning = true;
private boolean packageModified = false;

public CloudPaygManagerTestHelper() {
super();
}
public CloudPaygManagerTestHelper(TaskomaticApi tapiIn, ContentSyncManager syncManagerIn) {
super(tapiIn, syncManagerIn);
}

@Override
protected boolean isFileExecutable(String filename) {
return filename.equals("/usr/bin/instance-flavor-check") ||
filename.equals("/usr/bin/ec2metadata");
}

public void setInstanceType(String iTypeIn) {
iType = iTypeIn;
}

public void setRequestResult(String reqResultIn) {
reqResult = reqResultIn;
}

public void setServiceRunning(boolean isRunning) {
serviceRunning = isRunning;
}

public void setPackageModified(boolean pkgModifiedIn) {
packageModified = pkgModifiedIn;
}

protected String getInstanceType() {
return iType;
}
@Override
protected String requestUrl(String url) {
return reqResult;
}
@Override
protected boolean isServiceRunning(String serviceIn) {
return serviceRunning;
}
@Override
protected boolean hasPackageModifications(String pkg) {
return packageModified;
}
}

@Test
public void testCloudProvider() {
CloudPaygManager cpm = new CloudPaygManager();
Expand Down Expand Up @@ -112,41 +165,18 @@ protected boolean isFileExecutable(String filename) {

@Test
public void testIsPayg() {
CloudPaygManager cpm = new CloudPaygManager() {
@Override
protected boolean isFileExecutable(String filename) {
return filename.equals("/usr/bin/instance-flavor-check") ||
filename.equals("/usr/bin/ec2metadata");
}

@Override
protected String getInstanceType() {
return "PAYG";
}
};
CloudPaygManagerTestHelper cpm = new CloudPaygManagerTestHelper();
cpm.setInstanceType("PAYG");
assertTrue(cpm.isPaygInstance(), "Expecting a PAYG instance");
}

@Test
public void testRefresh() {
TaskomaticApiTestHelper tapi = new TaskomaticApiTestHelper();
tapi.setResult(new HashMap<>());
CloudPaygManager cpm = new CloudPaygManager(tapi, new ContentSyncManager()) {
@Override
protected boolean isFileExecutable(String filename) {
return filename.equals("/usr/bin/instance-flavor-check") ||
filename.equals("/usr/bin/ec2metadata");
}

@Override
protected String getInstanceType() {
return "PAYG";
}
@Override
protected String requestUrl(String url) {
return "online";
}
};
CloudPaygManagerTestHelper cpm = new CloudPaygManagerTestHelper(tapi, new ContentSyncManager());
cpm.setInstanceType("PAYG");
cpm.setRequestResult("online");

assertTrue(cpm.checkRefreshCache(false), "Not refreshed");

Expand All @@ -161,22 +191,11 @@ public void testRefreshIsCompliant() {
TaskomaticApiTestHelper tapi = new TaskomaticApiTestHelper();
tapi.setNull();

CloudPaygManager cpm = new CloudPaygManager(tapi, new ContentSyncManager()) {
@Override
protected boolean isFileExecutable(String filename) {
return filename.equals("/usr/bin/instance-flavor-check") ||
filename.equals("/usr/bin/ec2metadata");
}

@Override
protected String getInstanceType() {
return "PAYG";
}
@Override
protected String requestUrl(String url) {
return "online";
}
};
CloudPaygManagerTestHelper cpm = new CloudPaygManagerTestHelper(tapi, new ContentSyncManager());
cpm.setInstanceType("PAYG");
cpm.setRequestResult("online");
cpm.setServiceRunning(true);
cpm.setPackageModified(false);

// test 1 - no schedule available
assertTrue(cpm.checkRefreshCache(false), "Not refreshed");
Expand All @@ -194,27 +213,27 @@ protected String requestUrl(String url) {
// test cache is used
assertFalse(cpm.checkRefreshCache(false), "Unexpected: refresh happened");

cpm = new CloudPaygManager(tapi, new ContentSyncManager()) {
@Override
protected boolean isFileExecutable(String filename) {
return filename.equals("/usr/bin/instance-flavor-check") ||
filename.equals("/usr/bin/ec2metadata");
}
// test 4 - billing-data-service is down
cpm.setRequestResult("error");
assertTrue(cpm.checkRefreshCache(true), "Not refreshed");
assertFalse(cpm.isCompliant(), "Unexpected: Is compliant");

@Override
protected String getInstanceType() {
return "PAYG";
}
@Override
protected String requestUrl(String url) {
return "error";
}
};
// test 5 - packages are modified
cpm.setRequestResult("online");
cpm.setPackageModified(true);
assertTrue(cpm.checkRefreshCache(false), "Not refreshed");
assertFalse(cpm.isCompliant(), "Unexpected: Is compliant");

// test 4 - billing-data-service is down
// test 6 - billing adapter is down
cpm.setPackageModified(false);
cpm.setServiceRunning(false);
assertTrue(cpm.checkRefreshCache(false), "Not refreshed");
assertFalse(cpm.isCompliant(), "Unexpected: Is compliant");

cpm.setServiceRunning(true);
assertTrue(cpm.checkRefreshCache(false), "Not refreshed");
assertTrue(cpm.isCompliant(), "Unexpected: Is not compliant");

}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- add compliance checks when running as PAYG
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- require csp-billing-adapter service
1 change: 1 addition & 0 deletions python/billingdataservice/billing-data-service.spec
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Source: %{name}-%{version}.tar.gz
BuildArch: noarch
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Requires: apache2
Requires: csp-billing-adapter-service
Requires: python3-Flask
Requires: spacewalk-backend-sql
Requires: spacewalk-taskomatic
Expand Down
1 change: 1 addition & 0 deletions python/billingdataservice/payg-service-override.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[Unit]
Requires=billing-data-service.service
Requires=csp-billing-adapter.service
Loading
Loading