package com.intrbiz.bergamot.importer; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; import org.apache.log4j.Logger; import com.codahale.metrics.Timer; import com.intrbiz.Util; import com.intrbiz.bergamot.config.model.AccessControlCfg; import com.intrbiz.bergamot.config.model.ActiveCheckCfg; import com.intrbiz.bergamot.config.model.BergamotCfg; import com.intrbiz.bergamot.config.model.CheckCfg; import com.intrbiz.bergamot.config.model.ClusterCfg; import com.intrbiz.bergamot.config.model.CommandCfg; import com.intrbiz.bergamot.config.model.ContactCfg; import com.intrbiz.bergamot.config.model.CredentialCfg; import com.intrbiz.bergamot.config.model.EscalateCfg; import com.intrbiz.bergamot.config.model.GroupCfg; import com.intrbiz.bergamot.config.model.HostCfg; import com.intrbiz.bergamot.config.model.LocationCfg; import com.intrbiz.bergamot.config.model.NamedObjectCfg; import com.intrbiz.bergamot.config.model.NotificationEngineCfg; import com.intrbiz.bergamot.config.model.NotificationsCfg; import com.intrbiz.bergamot.config.model.PassiveCheckCfg; import com.intrbiz.bergamot.config.model.RealCheckCfg; import com.intrbiz.bergamot.config.model.ResourceCfg; import com.intrbiz.bergamot.config.model.SecuredObjectCfg; import com.intrbiz.bergamot.config.model.SecurityDomainCfg; import com.intrbiz.bergamot.config.model.ServiceCfg; import com.intrbiz.bergamot.config.model.TeamCfg; import com.intrbiz.bergamot.config.model.TemplatedObjectCfg; import com.intrbiz.bergamot.config.model.TemplatedObjectCfg.ObjectState; import com.intrbiz.bergamot.config.model.TimePeriodCfg; import com.intrbiz.bergamot.config.model.TrapCfg; import com.intrbiz.bergamot.config.model.VirtualCheckCfg; import com.intrbiz.bergamot.config.validator.ValidatedBergamotConfiguration; import com.intrbiz.bergamot.data.BergamotDB; import com.intrbiz.bergamot.model.AccessControl; import com.intrbiz.bergamot.model.ActiveCheck; import com.intrbiz.bergamot.model.Check; import com.intrbiz.bergamot.model.CheckCommand; import com.intrbiz.bergamot.model.Cluster; import com.intrbiz.bergamot.model.Command; import com.intrbiz.bergamot.model.Config; import com.intrbiz.bergamot.model.Contact; import com.intrbiz.bergamot.model.Credential; import com.intrbiz.bergamot.model.Escalation; import com.intrbiz.bergamot.model.Group; import com.intrbiz.bergamot.model.Host; import com.intrbiz.bergamot.model.Location; import com.intrbiz.bergamot.model.NotificationEngine; import com.intrbiz.bergamot.model.Notifications; import com.intrbiz.bergamot.model.PassiveCheck; import com.intrbiz.bergamot.model.RealCheck; import com.intrbiz.bergamot.model.Resource; import com.intrbiz.bergamot.model.SecuredObject; import com.intrbiz.bergamot.model.SecurityDomain; import com.intrbiz.bergamot.model.Service; import com.intrbiz.bergamot.model.Site; import com.intrbiz.bergamot.model.Status; import com.intrbiz.bergamot.model.Team; import com.intrbiz.bergamot.model.TimePeriod; import com.intrbiz.bergamot.model.Trap; import com.intrbiz.bergamot.model.VirtualCheck; import com.intrbiz.bergamot.model.message.notification.Notification; import com.intrbiz.bergamot.model.message.notification.RegisterContactNotification; import com.intrbiz.bergamot.model.message.scheduler.EnableCheck; import com.intrbiz.bergamot.model.message.scheduler.RescheduleCheck; import com.intrbiz.bergamot.model.message.scheduler.ScheduleCheck; import com.intrbiz.bergamot.model.message.scheduler.SchedulerAction; import com.intrbiz.bergamot.model.message.scheduler.UnscheduleCheck; import com.intrbiz.bergamot.model.state.CheckState; import com.intrbiz.bergamot.model.state.CheckStats; import com.intrbiz.bergamot.queue.NotificationQueue; import com.intrbiz.bergamot.queue.SchedulerQueue; import com.intrbiz.bergamot.queue.key.NotificationKey; import com.intrbiz.bergamot.queue.key.SchedulerKey; import com.intrbiz.bergamot.virtual.VirtualCheckExpressionContext; import com.intrbiz.bergamot.virtual.VirtualCheckExpressionParser; import com.intrbiz.bergamot.virtual.operator.VirtualCheckOperator; import com.intrbiz.bergamot.virtual.reference.CheckReference; import com.intrbiz.configuration.CfgParameter; import com.intrbiz.configuration.Configuration; import com.intrbiz.data.DataException; import com.intrbiz.gerald.witchcraft.Witchcraft; import com.intrbiz.queue.RoutedProducer; public class BergamotConfigImporter { private BergamotImportReport report; private Site site; private BergamotCfg config; private boolean resetState = false; private boolean createSite = false; private boolean online = false; private Map<String, CascadedChange> cascadedChanges = new HashMap<String, CascadedChange>(); private Set<String> loadedObjects = new HashSet<String>(); private List<DelayedSchedulerAction> delayedSchedulerActions = new LinkedList<DelayedSchedulerAction>(); private List<Contact> delayedContactRegistrations = new LinkedList<Contact>(); private Function<Contact, String> registrationURLSupplier; private boolean rebuildPermissions = false; private boolean clearPermissionsCache = false; private Timer importTimer = Witchcraft.get().source("com.intrbiz.config.bergamot").getRegistry().timer(Witchcraft.name(BergamotConfigImporter.class, "import_time")); private String defaultPassword = "bergamot"; private boolean requirePasswordChange = true; public BergamotConfigImporter(ValidatedBergamotConfiguration validated) { if (! validated.getReport().isValid()) throw new RuntimeException("Cannot import invalid configuration"); this.config = validated.getConfig(); } public BergamotConfigImporter defaultPassword(String password) { this.defaultPassword = password; return this; } public BergamotConfigImporter requirePasswordChange(boolean change) { this.requirePasswordChange = change; return this; } public BergamotConfigImporter resetState(boolean resetState) { this.resetState = resetState; return this; } public BergamotConfigImporter createSite(boolean createSite) { this.createSite = createSite; return this; } public BergamotConfigImporter online(boolean online) { this.online = online; return this; } public BergamotConfigImporter online() { this.online = true; return this; } public BergamotConfigImporter offline() { this.online = false; return this; } public BergamotConfigImporter registrationURLSupplier(Function<Contact, String> registrationURLSupplier) { this.registrationURLSupplier = registrationURLSupplier; return this; } public BergamotImportReport importConfiguration() { if (this.report == null) { try (Timer.Context tctx = this.importTimer.time()) { this.report = new BergamotImportReport(this.config.getSite()); try { // update database try (BergamotDB db = BergamotDB.connect()) { db.execute(()-> { // setup the site this.loadSite(db); // compute any cascading changes this.computeCascade(db); // load any security domains this.loadSecurityDomains(db); // templates this.loadTemplates(db); // load the credentials this.loadCredentials(db); // load the commands this.loadCommands(db); // time periods this.loadTimePeriods(db); // teams this.loadTeams(db); // contacts this.loadContacts(db); // load the locations this.loadLocations(db); // groups this.loadGroups(db); // hosts this.loadHosts(db); // clusters this.loadClusters(db); // link any check to check dependencies this.linkDependencies(db); // rebuild computed permissions if (this.rebuildPermissions) { this.report.info("Rebuilding computed permissions"); db.buildPermissions(this.site.getId()); } if (this.clearPermissionsCache) { this.report.info("Clearing permissions cache"); db.invalidatePermissionsCache(this.site.getId()); } }); // delayed actions if (this.online) { // we must publish any scheduling changes after we have committed the transaction // publish all scheduling changes try (SchedulerQueue queue = SchedulerQueue.open()) { try (RoutedProducer<SchedulerAction, SchedulerKey> producer = queue.publishSchedulerActions()) { for (DelayedSchedulerAction delayedAction : this.delayedSchedulerActions) { producer.publish(delayedAction.key, delayedAction.action); } } } // we must publish and contact registration notifications try (NotificationQueue notificationQueue = NotificationQueue.open()) { try (RoutedProducer<Notification, NotificationKey> notificationsProducer = notificationQueue.publishNotifications()) { for (Contact contact : this.delayedContactRegistrations) { try { // get the registration url String url = this.registrationURLSupplier.apply(contact); // send a notification, only via email notificationsProducer.publish( new NotificationKey(contact.getSite().getId()), new RegisterContactNotification(contact.getSite().toMOUnsafe(), contact.toMOUnsafe().addEngine("email"), url) ); } catch (Exception e) { Logger.getLogger(BergamotConfigImporter.class).error("Failed to send registration notification", e); throw e; } } } } } } } catch (Throwable e) { Logger.getLogger(BergamotConfigImporter.class).error("Failed to import configuration", e); this.report.error("Configuration change aborted due to unhandled error: " + e.getMessage()); StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); this.report.error(sw.toString()); } } } return this.report; } private void loadSite(BergamotDB db) { this.site = db.getSiteByName(this.config.getSite()); if (this.site == null) { if (this.createSite) { this.site = new Site(); this.site.setId(Site.randomSiteId()); this.site.setName(this.config.getSite()); this.site.setSummary(this.config.getSite()); db.setSite(site); } else { this.report.error("Site '" + this.config.getSite() + "' does not exist and cannot be created, aborting!"); throw new RuntimeException("Site '" + this.config.getSite() + "' does not exist and cannot be created, aborting!"); } } // update any parameters if (! this.config.getParameters().isEmpty()) { for (CfgParameter parameter : this.config.getParameters()) { this.site.setParameter(parameter.getName(), parameter.getValueOrText()); } db.setSite(site); } } private void loadTemplates(BergamotDB db) { for (List<? extends TemplatedObjectCfg<?>> objects : this.config.getAllObjects()) { for (TemplatedObjectCfg<?> object : objects) { if (object.getTemplateBooleanValue() && object instanceof NamedObjectCfg) { String type = Configuration.getRootElement(((NamedObjectCfg<?>) object).getClass()); Config conf = db.getConfigByName(this.site.getId(), type, object.getName()); if (conf == null) { conf = new Config(this.site.randomObjectId(), this.site.getId(), (NamedObjectCfg<?>) object); this.report.info("Configuring new " + type + " template: " + object.getName()); db.setConfig(conf); // add the template to the correct security domains if (object instanceof SecuredObjectCfg) { this.linkSecurityDomains((SecuredObjectCfg<?>) object.resolve(), conf.getId(), db); } } else { if (ObjectState.isRemove(object.getObjectState())) { if (conf.listAllDependentsObjects().isEmpty()) { // nothing is using this template, so remove it this.report.info("Removing existing " + type + " template: " + object.getName() + " (" + conf.getId() + ")"); db.removeConfig(conf.getId()); db.removeSecurityDomainMembershipForCheck(conf.getId()); } else { // we cannot remove this template, it is in use throw new RuntimeException("The " + type + " template " + conf.getName() + " cannot be removed as it is in use."); } } else { conf.fromConfiguration((NamedObjectCfg<?>) object); this.report.info("Reconfiguring existing " + type + " template: " + object.getName() + " (" + conf.getId() + ")"); db.setConfig(conf); } } } } } } private void computeCascade(BergamotDB db) { for (List<? extends TemplatedObjectCfg<?>> objects : this.config.getAllObjects()) { for (TemplatedObjectCfg<?> object : objects) { String type = Configuration.getRootElement(((NamedObjectCfg<?>) object).getClass()); Config conf = db.getConfigByName(this.site.getId(), type, object.getName()); if (conf != null) { List<Config> dependents = conf.listAllDependentsObjects(); if (ObjectState.isRemove(object.getObjectState())) { if (! dependents.isEmpty()) { // currently we restrict cascading removal throw new RuntimeException("The " + type + " " + conf.getName() + " cannot be removed as it is in use by: " + dependents.stream().map((c) -> c.getType() + " " + c.getName()).collect(Collectors.joining(", "))); } } else { this.report.info(" Change to " + type + ":" + conf.getName()); // cascade - note this recursively queries for all objects affected by a change to this template for (Config config : dependents) { this.report.info(" cascades to: " + config.getType() + ":" + config.getName()); this.cascadedChanges.put(config.getQualifiedName(), new CascadedChange(object, conf, config)); } } } } } } private void loadSecurityDomains(BergamotDB db) { for (SecurityDomainCfg configuration : this.config.getSecurityDomains()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { this.removeSecurityDomain(configuration, db); } else { this.loadSecurityDomain(configuration, db); } } } } private void removeSecurityDomain(SecurityDomainCfg configuration, BergamotDB db) { this.report.info("Removing security domain: " + configuration.resolve().getName()); SecurityDomain securityDomain = db.getSecurityDomainByName(this.site.getId(), configuration.getName()); if (securityDomain != null) { db.removeSecurityDomain(securityDomain.getId()); db.removeConfig(securityDomain.getId()); } } private void loadSecurityDomain(SecurityDomainCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("security_domain:" + configuration.getName())) { this.report.info("Skipping reconfiguring security domain " + configuration.getName()); return; } // load SecurityDomain securityDomain = db.getSecurityDomainByName(this.site.getId(), configuration.getName()); if(securityDomain == null) { configuration.setId(this.site.randomObjectId()); securityDomain = new SecurityDomain(); this.report.info("Configuring new security domain: " + configuration.resolve().getName()); } else { configuration.setId(securityDomain.getId()); this.report.info("Reconfiguring existing security domain: " + configuration.resolve().getName() + " (" + configuration.getId() + ")"); } // apply the new config securityDomain.configure(configuration); // update db.setSecurityDomain(securityDomain); this.loadedObjects.add("security_domain:" + configuration.getName()); } private void loadCommands(BergamotDB db) { for (CommandCfg configuration : this.config.getCommands()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { // remove the command this.removeCommand(configuration, db); } else { // add or change the command this.loadCommand(configuration, db); } } } // load any commands where a template change cascades for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof CommandCfg) { CommandCfg configuration = (CommandCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("command:" + configuration.getName()))) { this.report.info("Reconfiguring command " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadCommand(configuration, db); } } } } private void removeCommand(CommandCfg configuration, BergamotDB db) { this.report.info("Removing command: " + configuration.resolve().getName()); // remove the command Command command = db.getCommandByName(this.site.getId(), configuration.getName()); if (command != null) { db.removeCommand(command.getId()); db.removeConfig(command.getId()); db.removeSecurityDomainMembershipForCheck(command.getId()); } } private void loadCommand(CommandCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("command:" + configuration.getName())) { this.report.info("Skipping reconfiguring command " + configuration.getName()); return; } // load CommandCfg resolvedConfiguration = configuration.resolve(); Command command = db.getCommandByName(this.site.getId(), configuration.getName()); if(command == null) { configuration.setId(this.site.randomObjectId()); command = new Command(); this.report.info("Configuring new command: " + resolvedConfiguration.getName()); } else { configuration.setId(command.getId()); this.report.info("Reconfiguring existing command: " + resolvedConfiguration.getName() + " (" + configuration.getId() + ")"); } // apply the new config command.configure(configuration); // update db.setCommand(command); this.loadedObjects.add("command:" + configuration.getName()); // security domains this.linkSecurityDomains(resolvedConfiguration, command, db); } private void loadLocations(BergamotDB db) { for (LocationCfg configuration : this.config.getLocations()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { // remove the location this.removeLocation(configuration, db); } else { // add or change the location this.loadLocation(configuration, db); } } } // link the tree for (LocationCfg configuration : this.config.getLocations()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isChange(configuration.getObjectState())) { // only link locations that are being changed or added this.linkLocation(configuration, db); } } } // load any location where a template change cascades for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof LocationCfg) { LocationCfg configuration = (LocationCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("location:" + configuration.getName()))) { this.report.info("Reconfiguring location " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadLocation(configuration, db); this.linkLocation(configuration, db); } } } } private void removeLocation(LocationCfg configuration, BergamotDB db) { this.report.info("Removing location: " + configuration.resolve().getName()); // remove the location Location location = db.getLocationByName(this.site.getId(), configuration.getName()); if (location != null) { db.removeLocation(location.getId()); db.removeConfig(location.getId()); db.removeSecurityDomainMembershipForCheck(location.getId()); } } private void loadLocation(LocationCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("location:" + configuration.getName())) { this.report.info("Skipping reconfiguring location " + configuration.getName()); return; } // load LocationCfg resolvedConfiguration = configuration.resolve(); Location location = db.getLocationByName(this.site.getId(), configuration.getName()); if (location == null) { configuration.setId(this.site.randomObjectId()); location = new Location(); this.report.info("Configuring new location: " + resolvedConfiguration.getName()); } else { configuration.setId(location.getId()); this.report.info("Reconfiguring existing location: " + resolvedConfiguration.getName() + " (" + configuration.getId() + ")"); } location.configure(configuration); db.setLocation(location); this.loadedObjects.add("location:" + configuration.getName()); // security domains this.linkSecurityDomains(resolvedConfiguration, location, db); } private void linkLocation(LocationCfg configuration, BergamotDB db) { Location l = db.getLocation(configuration.getId()); if (l != null) { String pn = configuration.resolve().getLocation(); if (!Util.isEmpty(pn)) { Location p = db.getLocationByName(this.site.getId(), pn); if (p != null) { this.report.info("Adding location " + l.getName() + " to location " + p.getName()); db.addLocationChild(p, l); } } } } private void loadGroups(BergamotDB db) { for (GroupCfg configuration : this.config.getGroups()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { this.removeGroup(configuration, db); } else { this.loadGroup(configuration, db); } } } // link the tree for (GroupCfg configuration : this.config.getGroups()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isChange(configuration.getObjectState())) { this.linkGroup(configuration, db); } } } // load any group where a template change cascades for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof GroupCfg) { GroupCfg configuration = (GroupCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("group:" + configuration.getName()))) { this.report.info("Reconfiguring group " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadGroup(configuration, db); this.linkGroup(configuration, db); } } } } private void removeGroup(GroupCfg configuration, BergamotDB db) { this.report.info("Removing group: " + configuration.resolve().getName()); // remove the group Group group = db.getGroupByName(this.site.getId(), configuration.getName()); if (group != null) { db.removeGroup(group.getId()); db.removeConfig(group.getId()); db.removeSecurityDomainMembershipForCheck(group.getId()); } } private void loadGroup(GroupCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("group:" + configuration.getName())) { this.report.info("Skipping reconfiguring group " + configuration.getName()); return; } GroupCfg resolvedConfiguration = configuration.resolve(); // load Group group = db.getGroupByName(this.site.getId(), configuration.getName()); if (group == null) { configuration.setId(this.site.randomObjectId()); group = new Group(); this.report.info("Configuring new group: " + resolvedConfiguration.getName()); } else { configuration.setId(group.getId()); this.report.info("Reconfiguring existing group: " + resolvedConfiguration.getName() + " (" + configuration.getId() + ")"); } group.configure(configuration); db.setGroup(group); this.loadedObjects.add("group:" + configuration.getName()); // security domains this.linkSecurityDomains(resolvedConfiguration, group, db); } private void linkGroup(GroupCfg configuration, BergamotDB db) { Group child = db.getGroup(configuration.getId()); if (child != null) { for (String parentName : configuration.resolve().getGroups()) { Group parent = db.getGroupByName(this.site.getId(), parentName); if (parent != null) { this.report.info("Adding group " + child.getName() + " to group " + parent.getName()); db.addGroupChild(parent, child); } } } } private void loadTimePeriods(BergamotDB db) { for (TimePeriodCfg configuration : this.config.getTimePeriods()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { this.removeTimePeriod(configuration, db); } else { this.loadTimePeriod(configuration, db); } } } // link excludes for (TimePeriodCfg configuration : this.config.getTimePeriods()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isChange(configuration.getObjectState())) { this.linkTimePeriod(configuration, db); } } } // load any timeperiod where a template change cascades for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof TimePeriodCfg) { TimePeriodCfg configuration = (TimePeriodCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("timeperiod:" + configuration.getName()))) { this.report.info("Reconfiguring time period " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadTimePeriod(configuration, db); this.linkTimePeriod(configuration, db); } } } } private void removeTimePeriod(TimePeriodCfg configuration, BergamotDB db) { this.report.info("Removing timeperiod: " + configuration.resolve().getName()); TimePeriod timePeriod = db.getTimePeriodByName(this.site.getId(), configuration.getName()); if (timePeriod != null) { db.removeTimePeriod(timePeriod.getId()); db.removeConfig(timePeriod.getId()); db.removeSecurityDomainMembershipForCheck(timePeriod.getId()); } } private void loadTimePeriod(TimePeriodCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("timeperiod:" + configuration.getName())) { this.report.info("Skipping reconfiguring timeperiod " + configuration.getName()); return; } // load TimePeriodCfg resolvedConfiguration = configuration.resolve(); TimePeriod timePeriod = db.getTimePeriodByName(this.site.getId(), configuration.getName()); if (timePeriod == null) { configuration.setId(this.site.randomObjectId()); timePeriod = new TimePeriod(); this.report.info("Configuring new timeperiod: " + resolvedConfiguration.getName()); } else { configuration.setId(timePeriod.getId()); this.report.info("Reconfiguring existing timeperiod: " + resolvedConfiguration.getName() + " (" + configuration.getId() + ")"); } timePeriod.configure(configuration); db.setTimePeriod(timePeriod); this.loadedObjects.add("timeperiod:" + configuration.getName()); // security domains this.linkSecurityDomains(resolvedConfiguration, timePeriod, db); } private void linkTimePeriod(TimePeriodCfg configuration, BergamotDB db) { TimePeriod timePeriod = db.getTimePeriod(configuration.getId()); if (timePeriod != null) { for (String excludeName : configuration.resolve().getExcludes()) { TimePeriod excluded = db.getTimePeriodByName(this.site.getId(), excludeName); if (excluded != null) { this.report.info("Adding excluded timeperiod " + excluded.getName() + " to timeperiod " + timePeriod.getName()); db.addTimePeriodExclude(timePeriod, excluded); } } } } private void loadTeams(BergamotDB db) { for (TeamCfg configuration : this.config.getTeams()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { this.removeTeam(configuration, db); } else { this.loadTeam(configuration, db); } } } // link the tree for (TeamCfg configuration : this.config.getTeams()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isChange(configuration.getObjectState())) { this.linkTeam(configuration, db); } } } // load any team where a template change cascades for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof TeamCfg) { TeamCfg configuration = (TeamCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("team:" + configuration.getName()))) { this.report.info("Reconfiguring team " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadTeam(configuration, db); this.linkTeam(configuration, db); } } } } private void removeTeam(TeamCfg configuration, BergamotDB db) { this.report.info("Removing team: " + configuration.resolve().getName()); Team team = db.getTeamByName(this.site.getId(), configuration.getName()); if (team != null) { db.removeTeam(team.getId()); db.removeConfig(team.getId()); db.removeSecurityDomainMembershipForCheck(team.getId()); } } private void loadTeam(TeamCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("team:" + configuration.getName())) { this.report.info("Skipping reconfiguring team " + configuration.getName()); return; } // load TeamCfg resolvedConfiguration = configuration.resolve(); Team team = db.getTeamByName(this.site.getId(), configuration.getName()); if (team == null) { configuration.setId(this.site.randomObjectId()); team = new Team(); this.report.info("Configuring new team: " + resolvedConfiguration.getName()); } else { configuration.setId(team.getId()); this.report.info("Reconfiguring existing team: " + resolvedConfiguration.getName() + " (" + configuration.getId() + ")"); } team.configure(configuration); // access controls this.loadAccessControls(team.getId(), configuration.resolve().getAccessControls(), db); db.setTeam(team); this.loadedObjects.add("team:" + configuration.getName()); // we need to rebuild the permissions this.rebuildPermissions = true; // security domains this.linkSecurityDomains(resolvedConfiguration, team, db); } private void linkTeam(TeamCfg configuration, BergamotDB db) { Team child = db.getTeam(configuration.getId()); if (child != null) { for (String parentName : configuration.resolve().getTeams()) { Team parent = db.getTeamByName(this.site.getId(), parentName); if (parent != null) { this.report.info("Adding team " + child.getName() + " to team " + parent.getName()); db.addTeamChild(parent, child); } } } } private void loadContacts(BergamotDB db) { for (ContactCfg configuration : this.config.getContacts()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { this.removeContact(configuration, db); } else { this.loadContact(configuration, db); } } } // load any contact where a template change cascades for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof ContactCfg) { ContactCfg configuration = (ContactCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("team:" + configuration.getName()))) { this.report.info("Reconfiguring contact " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadContact(configuration, db); } } } } private void removeContact(ContactCfg configuration, BergamotDB db) { this.report.info("Removing contact: " + configuration.resolve().getName()); Contact contact = db.getContactByName(this.site.getId(), configuration.resolve().getName()); if (contact != null) { db.removeContact(contact.getId()); db.removeConfig(contact.getId()); db.removeSecurityDomainMembershipForCheck(contact.getId()); } } private void loadContact(ContactCfg configuration, BergamotDB db) { ContactCfg resolvedConfiguration = configuration.resolve(); // load Contact contact = db.getContactByName(this.site.getId(), resolvedConfiguration.getName()); if (contact == null) { configuration.setId(this.site.randomObjectId()); contact = new Contact(); // handle the password if (this.online) { /* * We are in online mode, ie: running from within the UI, * as such send a registration notification email. */ contact.resetPassword(); this.registerContact(contact); this.report.info("Sending registration notification to " + resolvedConfiguration.getName() + " (" + resolvedConfiguration.getEmail() + ")"); } else { /* * We are in offline mode, ie: running an import from the CLI / first install, * so simply set a default password of 'bergamot' for any users, * created. */ contact.hashPassword(this.defaultPassword); contact.setForcePasswordChange(this.requirePasswordChange); this.report.info("Setting default password for user " + resolvedConfiguration.getName() + " (" + resolvedConfiguration.getEmail() + ") to 'bergamot', please login and change it!"); } this.report.info("Configuring new contact: " + resolvedConfiguration.getName() + " (" + resolvedConfiguration.getEmail() + ")"); } else { configuration.setId(contact.getId()); this.report.info("Reconfiguring existing contact: " + configuration.resolve().getName() + " (" + configuration.getId() + ")"); } contact.configure(configuration); // notifications this.loadNotifications(contact.getId(), resolvedConfiguration.getNotifications(), db); // access controls this.loadAccessControls(contact.getId(), resolvedConfiguration.getAccessControls(), db); // store db.setContact(contact); this.loadedObjects.add("contact:" + configuration.getName()); // teams for (String teamName : resolvedConfiguration.getTeams()) { Team team = db.getTeamByName(this.site.getId(), teamName); if (team != null) { this.report.info("Adding contact " + contact.getName() + " to team " + team.getName()); team.addContact(contact); } } // we need to rebuild the permissions this.rebuildPermissions = true; // security domains this.linkSecurityDomains(resolvedConfiguration, contact, db); } private void loadAccessControls(UUID roleId, List<AccessControlCfg> acl, BergamotDB db) { for (AccessControlCfg cfg : acl) { SecurityDomain domain = db.getSecurityDomainByName(this.site.getId(), cfg.getSecurityDomain()); if (domain != null) { db.setAccessControl(new AccessControl(domain.getId(), roleId, new LinkedList<String>(cfg.getGrantedPermissions()), new LinkedList<String>(cfg.getRevokedPermissions()))); } } } private void loadNotifications(UUID owner, NotificationsCfg configuration, BergamotDB db) { Notifications notifications = new Notifications(); notifications.setId(owner); notifications.setEnabled(configuration.getEnabledBooleanValue()); notifications.setAlertsEnabled(configuration.getAlertsBooleanValue()); notifications.setRecoveryEnabled(configuration.getRecoveryBooleanValue()); notifications.setAcknowledgeEnabled(configuration.getAcknowledgeBooleanValue()); notifications.setIgnore(configuration.getIgnore().stream().map((e) -> {return Status.valueOf(e.toUpperCase());}).collect(Collectors.toList())); notifications.setAllEnginesEnabled(configuration.getAllEnginesEnabledBooleanValue()); // load the time period if (! Util.isEmpty(configuration.getNotificationPeriod())) { TimePeriod timePeriod = db.getTimePeriodByName(this.site.getId(), configuration.getNotificationPeriod()); if (timePeriod != null) { notifications.setTimePeriodId(timePeriod.getId()); } } db.setNotifications(notifications); // engines for (NotificationEngineCfg econfiguration : configuration.getNotificationEngines()) { NotificationEngine notificationEngine = new NotificationEngine(); notificationEngine.setNotificationsId(notifications.getId()); notificationEngine.setEngine(econfiguration.getEngine()); notificationEngine.setEnabled(econfiguration.getEnabledBooleanValue()); notificationEngine.setAlertsEnabled(econfiguration.getAlertsBooleanValue()); notificationEngine.setRecoveryEnabled(econfiguration.getRecoveryBooleanValue()); notificationEngine.setIgnore(econfiguration.getIgnore().stream().map(Status::parse).collect(Collectors.toList())); if (! Util.isEmpty(econfiguration.getNotificationPeriod())) { TimePeriod timePeriod = db.getTimePeriodByName(this.site.getId(), econfiguration.getNotificationPeriod()); if (timePeriod != null) { notificationEngine.setTimePeriodId(timePeriod.getId()); } } // db.setNotificationEngine(notificationEngine); } // escalations db.removeEscalations(owner); for (EscalateCfg ecfg : configuration.getEscalations()) { Escalation esc = new Escalation(); esc.setNotificationsId(owner); esc.setAfter(ecfg.getAfterTimeInterval().toMillis()); esc.setRenotify(ecfg.getRenotifyBooleanValue()); esc.setIgnore(ecfg.getIgnore().stream().map(Status::parse).collect(Collectors.toList())); // load the time period if (! Util.isEmpty(ecfg.getEscalationPeriod())) { TimePeriod timePeriod = db.getTimePeriodByName(this.site.getId(), ecfg.getEscalationPeriod()); if (timePeriod != null) { notifications.setTimePeriodId(timePeriod.getId()); } } // notify esc.getTeamIds().clear(); esc.getContactIds().clear(); if (ecfg.getNotify() != null) { for (String teamName : ecfg.getNotify().getTeams()) { Team team = db.getTeamByName(this.site.getId(), teamName); if (team != null) { esc.getTeamIds().add(team.getId()); } } for (String contactName : ecfg.getNotify().getContacts()) { Contact contact = db.getContactByName(this.site.getId(), contactName); if (contact != null) { esc.getContactIds().add(contact.getId()); } } } // store it db.setEscalation(esc); } } private void loadHosts(BergamotDB db) { // load all directly modified hosts for (HostCfg configuration : this.config.getHosts()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { this.removeHost(configuration, db); } else { this.loadHost(configuration, db); } } } // load any hosts where a template change cascades // Note: we don't need to separately handle service and trap // template changes as these are caught by reconfiguring // the host they exist on for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof HostCfg) { HostCfg configuration = (HostCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("host:" + configuration.getName()))) { this.report.info("Reconfiguring host " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadHost(configuration, db); } } } } private void removeHost(HostCfg configuration, BergamotDB db) { // remove this host and all its services this.report.info("Removing host: " + configuration.resolve().getName()); Host host = db.getHostByName(this.site.getId(), configuration.resolve().getName()); if (host != null) { // remove all services for (Service service : host.getServices()) { // remove this.unscheduleCheck(service); db.removeService(service.getId()); db.removeConfig(service.getId()); db.removeSecurityDomainMembershipForCheck(service.getId()); } // remove the host this.unscheduleCheck(host); db.removeHost(host.getId()); db.removeConfig(host.getId()); db.removeSecurityDomainMembershipForCheck(host.getId()); } } private void loadHost(HostCfg configuration, BergamotDB db) { HostCfg resolvedConfiguration = configuration.resolve(); if (this.loadedObjects.contains("host:" + configuration.getName())) { this.report.info("Skipping reconfiguring host " + configuration.getName()); return; } this.report.info("Loading host:\r\n" + resolvedConfiguration.toString()); // load boolean newHost = false; Host host = db.getHostByName(this.site.getId(), resolvedConfiguration.getName()); if (host == null) { configuration.setId(this.site.randomObjectId()); host = new Host(); newHost = true; this.report.info("Configuring new host: " + resolvedConfiguration.getName()); } else { configuration.setId(host.getId()); this.report.info("Reconfiguring existing host: " + resolvedConfiguration.getName() + " (" + configuration.getId() + ")"); } host.configure(configuration); // load the check details this.loadActiveCheck(host, resolvedConfiguration, db); // add locations if (host.getLocationId() != null) db.invalidateHostsInLocation(host.getLocationId()); host.setLocationId(null); String locationName = resolvedConfiguration.getLocation(); if (!Util.isEmpty(locationName)) { Location location = db.getLocationByName(this.site.getId(), locationName); if (location != null) { host.setLocationId(location.getId()); db.invalidateHostsInLocation(location.getId()); } } // add the host db.setHost(host); this.loadedObjects.add("host:" + configuration.getName()); // schedule if (newHost) { this.scheduleCheck(host); } else { this.rescheduleCheck(host); } // remove any services and traps which are not if (! newHost) { // index the services we want Set<String> wantedServices = resolvedConfiguration.getServices().stream().map((s) -> s.resolve().getName()).collect(Collectors.toSet()); // remove any unwanted services for (Service service : host.getServices()) { if (! wantedServices.contains(service.getName())) { report.info("Removing service " + service.getName() + " (" + service.getId() + ") as it is no longer given for this host"); // remove service this.unscheduleCheck(service); db.removeService(service.getId()); db.removeConfig(service.getId()); } } // index the traps we want Set<String> wantedTraps = resolvedConfiguration.getTraps().stream().map((t) -> t.resolve().getName()).collect(Collectors.toSet()); // remove any unwanted services for (Trap trap : host.getTraps()) { if (! wantedTraps.contains(trap.getName())) { report.info("Removing trap " + trap.getName() + " (" + trap.getId() + ") as it is no longer given for this host"); // remove trap db.removeTrap(trap.getId()); db.removeConfig(trap.getId()); } } } // add services for (ServiceCfg serviceConfiguration : resolvedConfiguration.getServices()) { if (ObjectState.isRemove(serviceConfiguration.getObjectState())) { this.removeService(host, serviceConfiguration, db); } else { this.loadService(host, serviceConfiguration, db); } } // add traps for (TrapCfg trapConfiguration : resolvedConfiguration.getTraps()) { if (ObjectState.isRemove(trapConfiguration.getObjectState())) { this.removeTrap(host, trapConfiguration, db); } else { this.loadTrap(host, trapConfiguration, db); } } // cache invalidation db.invalidateServicesOnHost(host.getId()); db.invalidateTrapsOnHost(host.getId()); } private void loadActiveCheck(ActiveCheck<?,?> check, ActiveCheckCfg<?> resolvedConfiguration, BergamotDB db) { this.loadRealCheck(check, resolvedConfiguration, db); // the check period if (! Util.isEmpty(resolvedConfiguration.getSchedule().getTimePeriod())) { TimePeriod timePeriod = db.getTimePeriodByName(this.site.getId(), resolvedConfiguration.getSchedule().getTimePeriod()); if (timePeriod != null) { check.setTimePeriodId(timePeriod.getId()); } } } private void loadCheckState(Check<?,?> check, CheckCfg<?> configuration, BergamotDB db) { CheckState state = db.getCheckState(check.getId()); if (state == null || this.resetState) { state = new CheckState(); state.setCheckId(check.getId()); state.configure(configuration); db.setCheckState(state); } } private void loadCheckStats(Check<?,?> check, CheckCfg<?> configuration, BergamotDB db) { CheckStats stats = db.getCheckStats(check.getId()); if (stats == null || this.resetState) { stats = new CheckStats(); stats.setCheckId(check.getId()); db.setCheckStats(stats); } } private void loadRealCheck(RealCheck<?,?> check, RealCheckCfg<?> resolvedConfiguration, BergamotDB db) { this.loadCheck(check, resolvedConfiguration, db); // the check command if (resolvedConfiguration.getCheckCommand() != null) { // lookup the command Command command = db.getCommandByName(this.site.getId(), resolvedConfiguration.getCheckCommand().getCommand()); if (command != null) { CheckCommand checkCommand = new CheckCommand(); checkCommand.setCheckId(check.getId()); checkCommand.configure(resolvedConfiguration.getCheckCommand()); checkCommand.setCommandId(command.getId()); db.setCheckCommand(checkCommand); this.report.info("Added command " + command.getName() + " to check " + check.getName()); } else { throw new DataException("The command " + resolvedConfiguration.getCheckCommand().getCommand() + " could not be found, needed by " + check.getName()); } } } private void loadCheck(Check<?,?> check, CheckCfg<?> resolvedConfiguration, BergamotDB db) { // set the processing pool check.setPool(this.site.computeProcessingPool(check.getId())); // the state this.loadCheckState(check, resolvedConfiguration, db); // the stats this.loadCheckStats(check, resolvedConfiguration, db); // notifications this.loadNotifications(check.getId(), resolvedConfiguration.getNotifications(), db); // notify check.getTeamIds().clear(); check.getContactIds().clear(); for (String teamName : resolvedConfiguration.getNotify().getTeams()) { Team team = db.getTeamByName(this.site.getId(), teamName); if (team != null) { check.getTeamIds().add(team.getId()); } } for (String contactName : resolvedConfiguration.getNotify().getContacts()) { Contact contact = db.getContactByName(this.site.getId(), contactName); if (contact != null) { check.getContactIds().add(contact.getId()); } } // the groups for (UUID oldGroupId : check.getGroupIds()) { db.invalidateChecksInGroup(oldGroupId); } check.getGroupIds().clear(); for (String groupName : resolvedConfiguration.getGroups()) { Group group = db.getGroupByName(this.site.getId(), groupName); if (group != null) { this.report.info("Adding check " + check.getName() + " to group " + group.getName()); check.getGroupIds().add(group.getId()); db.invalidateChecksInGroup(group.getId()); } } // security domains this.linkSecurityDomains(resolvedConfiguration, check, db); } private void removeService(Host host, ServiceCfg configuration, BergamotDB db) { this.report.info("Removing service: " + configuration.resolve().getName() + " on host " + host.getName()); Service service = db.getServiceOnHost(host.getId(), configuration.resolve().getName()); if (service != null) { // remove service this.unscheduleCheck(service); db.removeService(service.getId()); db.removeConfig(service.getId()); db.removeSecurityDomainMembershipForCheck(service.getId()); } } private void loadService(Host host, ServiceCfg configuration, BergamotDB db) { // resolve ServiceCfg resolvedConfiguration = configuration.resolve(); // create the service boolean newService = false; Service service = db.getServiceOnHost(host.getId(), resolvedConfiguration.getName()); if (service == null) { configuration.setId(this.site.randomObjectId()); service = new Service(); service.setHostId(host.getId()); newService = true; this.report.info("Configuring new service: " + configuration.resolve().getName() + " on host " + host.getName()); } else { configuration.setId(service.getId()); this.report.info("Reconfiguring existing service: " + configuration.resolve().getName() + " on host " + host.getName() + " (" + configuration.getId() + ")"); } service.configure(configuration); // load the check details this.loadActiveCheck(service, resolvedConfiguration, db); // add db.setService(service); // schedule if (newService) { // schedule this new check this.scheduleCheck(service); } else { // reschedule this reconfigured check this.rescheduleCheck(service); } } private void removeTrap(Host host, TrapCfg configuration, BergamotDB db) { this.report.info("Removing trap: " + configuration.resolve().getName() + " on host " + host.getName()); Trap trap = db.getTrapOnHost(host.getId(), configuration.getName()); if (trap != null) { db.removeTrap(trap.getId()); db.removeConfig(trap.getId()); db.removeSecurityDomainMembershipForCheck(trap.getId()); } } private void loadTrap(Host host, TrapCfg configuration, BergamotDB db) { // resolve TrapCfg resolvedConfiguration = configuration.resolve(); // create the service Trap trap = db.getTrapOnHost(host.getId(), configuration.getName()); if (trap == null) { configuration.setId(this.site.randomObjectId()); trap = new Trap(); trap.setHostId(host.getId()); this.report.info("Configuring new trap: " + configuration.resolve().getName() + " on host " + host.getName()); } else { configuration.setId(trap.getId()); this.report.info("Reconfiguring existing trap: " + configuration.resolve().getName() + " on host " + host.getName() + " (" + configuration.getId() + ")"); } trap.configure(configuration); // load the check details this.loadPasiveCheck(trap, resolvedConfiguration, db); // add db.setTrap(trap); } private void loadPasiveCheck(PassiveCheck<?,?> check, PassiveCheckCfg<?> resolvedConfiguration, BergamotDB db) { this.loadRealCheck(check, resolvedConfiguration, db); } private void loadVirtualCheck(VirtualCheck<?,?> check, VirtualCheckCfg<?> resolvedConfiguration, BergamotDB db) { this.loadCheck(check, resolvedConfiguration, db); // parse the condition if (! Util.isEmpty(resolvedConfiguration.getCondition())) { VirtualCheckOperator cond = VirtualCheckExpressionParser.parseVirtualCheckExpression(resolvedConfiguration.getCondition()); if (cond != null) { // context to use VirtualCheckExpressionContext vcec = db.createVirtualCheckContext(this.site.getId(), null); // validate the condition for (CheckReference chkRef : cond.computeDependencies()) { if (chkRef.resolve(vcec) == null) { throw new RuntimeException("The virtual check " + check.getType() + " " + check.getName() + " is referencing checks which do not exist: " + chkRef.toString()); } } // set the condition check.setCondition(cond); this.report.info("Using virtual check condition " + cond.toString() + " for " + check); // cross reference the checks check.setReferenceIds(new LinkedList<UUID>(cond.computeDependencies().stream().map((c) -> c.resolve(vcec).getId()).collect(Collectors.toSet()))); } } } private void loadClusters(BergamotDB db) { for (ClusterCfg configuration : this.config.getClusters()) { if (!configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { this.removeCluster(configuration, db); } else { this.loadCluster(configuration, db); } } } // load any clusters where a template change cascades // Note: we don't need to separately handle resources // template changes as these are caught by reconfiguring // the cluster they exist on for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof ClusterCfg) { ClusterCfg configuration = (ClusterCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("cluster:" + configuration.getName()))) { this.report.info("Reconfiguring cluster " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadCluster(configuration, db); } } } } private void removeCluster(ClusterCfg configuration, BergamotDB db) { this.report.info("Removing cluster: " + configuration.resolve().getName()); Cluster cluster = db.getClusterByName(this.site.getId(), configuration.getName()); if (cluster != null) { for (Resource resource : cluster.getResources()) { db.removeResource(resource.getId()); db.removeConfig(resource.getId()); db.removeSecurityDomainMembershipForCheck(cluster.getId()); } db.removeCluster(cluster.getId()); db.removeConfig(cluster.getId()); db.removeSecurityDomainMembershipForCheck(cluster.getId()); } } private void loadCluster(ClusterCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("cluster:" + configuration.getName())) { this.report.info("Skipping reconfiguring cluster " + configuration.getName()); return; } // resolved config ClusterCfg resolvedConfiguration = configuration.resolve(); // load boolean newCluster = false; Cluster cluster = db.getClusterByName(this.site.getId(), configuration.getName()); if (cluster == null) { configuration.setId(this.site.randomObjectId()); cluster = new Cluster(); newCluster = true; this.report.info("Configuring new cluster: " + configuration.resolve().getName()); } else { configuration.setId(cluster.getId()); } cluster.configure(configuration); // load the check details this.loadVirtualCheck(cluster, resolvedConfiguration, db); // add the cluster db.setCluster(cluster); this.loadedObjects.add("cluster:" + configuration.getName()); // remove any resources which are not wanted if (! newCluster) { // index the resource we want Set<String> wantedResources = resolvedConfiguration.getResources().stream().map((r) -> r.resolve().getName()).collect(Collectors.toSet()); // remove any unwanted services for (Resource resource : cluster.getResources()) { if (! wantedResources.contains(resource.getName())) { report.info("Removing resource " + resource.getName() + " (" + resource.getId() + ") as it is no longer given for this cluster"); // remove resource db.removeResource(resource.getId()); db.removeConfig(resource.getId()); } } } // add resources for (ResourceCfg resourceConfiguration : resolvedConfiguration.getResources()) { if (ObjectState.isRemove(resourceConfiguration.getObjectState())) { this.removeResource(cluster, resourceConfiguration, db); } else { this.loadResource(cluster, resourceConfiguration, db); } } // cache invalidation db.invalidateResourcesOnCluster(cluster.getId()); } private void removeResource(Cluster cluster, ResourceCfg configuration, BergamotDB db) { this.report.info("Removing resource: " + configuration.resolve().getName() + " on cluster " + cluster.getName()); Resource resource = db.getResourceOnCluster(cluster.getId(), configuration.resolve().getName()); if (resource != null) { db.removeResource(resource.getId()); db.removeConfig(resource.getId()); db.removeSecurityDomainMembershipForCheck(resource.getId()); } } private void loadResource(Cluster cluster, ResourceCfg configuration, BergamotDB db) { // resolve ResourceCfg resolvedConfiguration = configuration.resolve(); // create the service Resource resource = db.getResourceOnCluster(cluster.getId(), resolvedConfiguration.getName()); if (resource == null) { configuration.setId(this.site.randomObjectId()); resource = new Resource(); resource.setClusterId(cluster.getId()); this.report.info("Configuring new resource: " + configuration.resolve().getName() + " on cluster " + cluster.getName()); } else { configuration.setId(resource.getId()); this.report.info("Reconfiguring existing group: " + configuration.resolve().getName() + " on cluster " + cluster.getName() + " (" + configuration.getId() + ")"); } resource.configure(configuration); // load the check details this.loadVirtualCheck(resource, resolvedConfiguration, db); // add db.setResource(resource); } protected void linkSecurityDomains(SecuredObjectCfg<?> resolvedConfiguration, SecuredObject<?,?> object, BergamotDB db) { this.linkSecurityDomains(resolvedConfiguration, object.getId(), db); } protected void linkSecurityDomains(SecuredObjectCfg<?> resolvedConfiguration, UUID objectId, BergamotDB db) { // set security domain membership db.removeSecurityDomainMembershipForCheck(objectId); for (String securityDomainName : resolvedConfiguration.getSecurityDomains()) { SecurityDomain domain = db.getSecurityDomainByName(this.site.getId(), securityDomainName); if (domain != null) db.addCheckToSecurityDomain(domain.getId(), objectId); } // ensure we flush cached permissions this.clearPermissionsCache = true; } protected void linkDependencies(BergamotDB db) { for (List<? extends TemplatedObjectCfg<?>> objects : this.config.getAllObjects()) { for (TemplatedObjectCfg<?> object : objects) { if ((! object.getTemplateBooleanValue()) && object instanceof RealCheckCfg<?>) { RealCheckCfg<?> checkCfg = (RealCheckCfg<?>) object; if (! Util.isEmpty(checkCfg.getDepends())) { // we only need to link dependencies on host and cluster // as services auto depend on host and resources auto depend on cluster if (checkCfg instanceof HostCfg) { Host host = db.getHostByName(this.site.getId(), checkCfg.getName()); // did we find the check if (host != null) { // parse the dependencies List<CheckReference> dependsOn = VirtualCheckExpressionParser.parseParentsExpression(checkCfg.getDepends()); // validate the references and link VirtualCheckExpressionContext vcec = db.createVirtualCheckContext(this.site.getId(), host); for (CheckReference chkRef : dependsOn) { Check<?,?> dependsOnCheck = chkRef.resolve(vcec); if (dependsOnCheck == null) throw new RuntimeException("The check " + checkCfg.getName() + " depends upon the check " + chkRef + " which does not exist"); host.getDependsIds().add(dependsOnCheck.getId()); } // this.report.info("The check " + checkCfg.getName() + "(" + host.getId() + ") depends upon " + host.getDependsIds()); // update db.setCheck(host); } } } } } } } private void loadCredentials(BergamotDB db) { for (CredentialCfg configuration : this.config.getCredentials()) { if (! configuration.getTemplateBooleanValue()) { if (ObjectState.isRemove(configuration.getObjectState())) { // remove the credential this.removeCredential(configuration, db); } else { // add or change the credential this.loadCredential(configuration, db); } } } // load any credential where a template change cascades for (CascadedChange change : this.cascadedChanges.values()) { if (change.dependent.getConfiguration() instanceof CredentialCfg) { CredentialCfg configuration = (CredentialCfg) change.dependent.getConfiguration(); if (! (configuration.getTemplateBooleanValue() || this.loadedObjects.contains("credential:" + configuration.getName()))) { this.report.info("Reconfiguring credential " + configuration.getName() + " due to a change to the " + change.template.getName() + " inherited template."); // first we need to resolve the inheritance for the cascaded object db.getConfigResolver(this.site.getId()).computeInheritenance(configuration); // load this.loadCredential(configuration, db); } } } } private void removeCredential(CredentialCfg configuration, BergamotDB db) { this.report.info("Removing credential: " + configuration.resolve().getName()); // remove the location Credential credential = db.getCredentialByName(this.site.getId(), configuration.getName()); if (credential != null) { db.removeCredential(credential.getId()); db.removeConfig(credential.getId()); db.removeSecurityDomainMembershipForCheck(credential.getId()); } } private void loadCredential(CredentialCfg configuration, BergamotDB db) { if (this.loadedObjects.contains("credential:" + configuration.getName())) { this.report.info("Skipping reconfiguring credential " + configuration.getName()); return; } // load CredentialCfg resolvedConfiguration = configuration.resolve(); Credential credential = db.getCredentialByName(this.site.getId(), configuration.getName()); if (credential == null) { configuration.setId(this.site.randomObjectId()); credential = new Credential(); this.report.info("Configuring new credential: " + resolvedConfiguration.getName()); } else { configuration.setId(credential.getId()); this.report.info("Reconfiguring existing credential: " + resolvedConfiguration.getName() + " (" + configuration.getId() + ")"); } credential.configure(configuration); db.setCredential(credential); this.loadedObjects.add("credential:" + configuration.getName()); // security domains this.linkSecurityDomains(resolvedConfiguration, credential, db); } /** * Queue the given contact to have a registration email sent */ private void registerContact(Contact contact) { this.delayedContactRegistrations.add(contact); } private void scheduleCheck(ActiveCheck<?,?> check) { this.report.info("Sscheduling " + check.getType() + " " + check.getName() + " (" + check.getId() + ")"); this.delayedSchedulerActions.add(new DelayedSchedulerAction(new SchedulerKey(check.getSiteId(), check.getPool()), new ScheduleCheck(check.getId()))); this.delayedSchedulerActions.add(new DelayedSchedulerAction(new SchedulerKey(check.getSiteId(), check.getPool()), new EnableCheck(check.getId()))); } private void rescheduleCheck(ActiveCheck<?,?> check) { this.report.info("Rescheduling " + check.getType() + " " + check.getName() + " (" + check.getId() + ")"); this.delayedSchedulerActions.add(new DelayedSchedulerAction(new SchedulerKey(check.getSiteId(), check.getPool()), new RescheduleCheck(check.getId()))); this.delayedSchedulerActions.add(new DelayedSchedulerAction(new SchedulerKey(check.getSiteId(), check.getPool()), new EnableCheck(check.getId()))); } private void unscheduleCheck(ActiveCheck<?,?> check) { this.report.info("Unscheduling " + check.getType() + " " + check.getName() + " (" + check.getId() + ")"); this.delayedSchedulerActions.add(new DelayedSchedulerAction(new SchedulerKey(check.getSiteId(), check.getPool()), new UnscheduleCheck(check.getId()))); } private static class CascadedChange { public final Config dependent; public final Config template; @SuppressWarnings("unused") public final TemplatedObjectCfg<?> change; public CascadedChange(TemplatedObjectCfg<?> change, Config template, Config dependent) { this.change = change; this.template = template; this.dependent = dependent; } } public static class DelayedSchedulerAction { public SchedulerKey key; public SchedulerAction action; public DelayedSchedulerAction(SchedulerKey key, SchedulerAction action) { this.key = key; this.action = action; } } }