extends mgmt simulator. Extended scalabaility of dmf listener.

Signed-off-by: Kai Zimmermann <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2016-06-15 10:45:34 +02:00
parent 239e9ff514
commit ecd5647438
15 changed files with 315 additions and 121 deletions

View File

@@ -12,6 +12,8 @@ import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
@@ -22,42 +24,53 @@ import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.support.RetryTemplate;
/**
* The spring AMQP configuration to use a AMQP for communication with SP update
* server.
*
*
*
*/
@Configuration
@EnableConfigurationProperties(AmqpProperties.class)
public class AmqpConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class);
@Autowired
protected AmqpProperties amqpProperties;
@Autowired
private ConnectionFactory connectionFactory;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* Create jackson message converter bean.
*
* @return the jackson message converter
* @return {@link RabbitTemplate} with automatic retry, published confirms
* and {@link Jackson2JsonMessageConverter}.
*/
@Bean
public MessageConverter jsonMessageConverter() {
final Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
return jackson2JsonMessageConverter;
public RabbitTemplate rabbitTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
final RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
rabbitTemplate.setRetryTemplate(retryTemplate);
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
LOGGER.debug("Message with correlation ID {} confirmed by broker.", correlationData.getId());
} else {
LOGGER.error("Broker is unable to handle message with correlation ID {} : {}", correlationData.getId(),
cause);
}
});
return rabbitTemplate;
}
/**
@@ -138,8 +151,8 @@ public class AmqpConfiguration {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(false);
containerFactory.setConnectionFactory(connectionFactory);
containerFactory.setConcurrentConsumers(20);
containerFactory.setMaxConcurrentConsumers(20);
containerFactory.setConcurrentConsumers(3);
containerFactory.setMaxConcurrentConsumers(10);
containerFactory.setPrefetchCount(20);
return containerFactory;
}

View File

@@ -8,9 +8,14 @@
*/
package org.eclipse.hawkbit.simulator.amqp;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
import org.springframework.beans.factory.annotation.Autowired;
@@ -22,6 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired;
*/
public abstract class SenderService extends MessageService {
private static final Logger LOGGER = LoggerFactory.getLogger(SenderService.class);
/**
* Constructor for sender service.
*
@@ -40,18 +47,25 @@ public abstract class SenderService extends MessageService {
/**
* Send a message if the message is not null.
*
* @param adress
* @param address
* the exchange name
* @param message
* the amqp message which will be send if its not null
*/
public void sendMessage(final String adress, final Message message) {
public void sendMessage(final String address, final Message message) {
if (message == null) {
return;
}
message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
rabbitTemplate.setExchange(adress);
rabbitTemplate.send(message);
final String correlationId = UUID.randomUUID().toString();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, address, correlationId);
} else {
LOGGER.debug("Sending message to exchange {} with correlationId {}", address, correlationId);
}
rabbitTemplate.send(address, null, message, new CorrelationData(correlationId));
}
/**

View File

@@ -35,14 +35,21 @@ import com.google.common.collect.Lists;
public class SpReceiverService extends ReceiverService {
private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class);
public static final String SOFTWARE_MODULE_FIRMWARE = "firmware";
private final SpSenderService spSenderService;
private final DeviceSimulatorUpdater deviceUpdater;
/**
* Constructor.
*
* @param rabbitTemplate
* for sending messages
* @param amqpProperties
* for amqp configuration
* @param spSenderService
* to send messages
* @param deviceUpdater
* simulator service for updates
*/
@Autowired
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,

View File

@@ -19,7 +19,9 @@
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
<Logger name="org.eclipse" level="DEBUG" />
<!-- Security Log with hints on potential attacks -->
<logger name="server-security" level="INFO" />

View File

@@ -48,7 +48,7 @@ public class Application implements CommandLineRunner {
private ClientConfigurationProperties configuration;
@Autowired
private ConfigurableScenario configurableScenario;
private ConfigurableScenario configuredScenario;
@Autowired
private CreateStartedRolloutExample gettingStartedRolloutScenario;
@@ -63,9 +63,8 @@ public class Application implements CommandLineRunner {
// run the create and start rollout example
gettingStartedRolloutScenario.run();
} else {
// run the getting started scenario which creates a setup of
// distribution set and software modules to be used
configurableScenario.run();
// run the configured scenario from properties
configuredScenario.run();
}
}
@@ -107,4 +106,4 @@ public class Application implements CommandLineRunner {
}
return false;
}
}
}

View File

@@ -39,8 +39,13 @@ public class ClientConfigurationProperties {
private final List<Scenario> scenarios = new ArrayList<>();
/**
* Simulation {@link Scenario}.
*
*/
public static class Scenario {
private String tenant = "DEFAULT";
private boolean cleanRepository;
private int targets = 100;
private int distributionSets = 10;
private int appModulesPerDistributionSet = 2;
@@ -50,6 +55,8 @@ public class ClientConfigurationProperties {
private String targetName = "Device";
private int artifactsPerSM = 1;
private String targetAddress = "amqp:/simulator.replyTo";
private boolean runRollouts = true;
private int rolloutDeploymentGroups = 4;
/**
* Artifact size. Values can use the suffixed "MB" or "KB" to indicate a
@@ -57,6 +64,30 @@ public class ClientConfigurationProperties {
*/
private String artifactSize = "1MB";
public boolean isCleanRepository() {
return cleanRepository;
}
public void setCleanRepository(final boolean cleanRepository) {
this.cleanRepository = cleanRepository;
}
public int getRolloutDeploymentGroups() {
return rolloutDeploymentGroups;
}
public void setRolloutDeploymentGroups(final int rolloutDeploymentGroups) {
this.rolloutDeploymentGroups = rolloutDeploymentGroups;
}
public boolean isRunRollouts() {
return runRollouts;
}
public void setRunRollouts(final boolean runRollouts) {
this.runRollouts = runRollouts;
}
public String getTargetAddress() {
return targetAddress;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
x * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -10,20 +10,25 @@ package org.eclipse.hawkbit.mgmt.client.scenarios;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.mgmt.client.ClientConfigurationProperties;
import org.eclipse.hawkbit.mgmt.client.ClientConfigurationProperties.Scenario;
import org.eclipse.hawkbit.mgmt.client.resource.MgmtDistributionSetClientResource;
import org.eclipse.hawkbit.mgmt.client.resource.MgmtRolloutClientResource;
import org.eclipse.hawkbit.mgmt.client.resource.MgmtSoftwareModuleClientResource;
import org.eclipse.hawkbit.mgmt.client.resource.MgmtSystemManagementClientResource;
import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetClientResource;
import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetBuilder;
import org.eclipse.hawkbit.mgmt.client.resource.builder.RolloutBuilder;
import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleAssigmentBuilder;
import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleBuilder;
import org.eclipse.hawkbit.mgmt.client.resource.builder.TargetBuilder;
import org.eclipse.hawkbit.mgmt.client.scenarios.upload.ArtifactFile;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutResponseBody;
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -53,7 +58,7 @@ public class ConfigurableScenario {
private MgmtTargetClientResource targetResource;
@Autowired
private MgmtSystemManagementClientResource systemManagementResource;
private MgmtRolloutClientResource rolloutResource;
@Autowired
private ClientConfigurationProperties clientConfigurationProperties;
@@ -65,16 +70,93 @@ public class ConfigurableScenario {
LOGGER.info("Running Configurable Scenario...");
clientConfigurationProperties.getScenarios().parallelStream().forEach(this::createScenario);
clientConfigurationProperties.getScenarios().forEach(this::createScenario);
}
private void createScenario(final Scenario scenario) {
systemManagementResource.deleteTenant(scenario.getTenant());
if (scenario.isCleanRepository()) {
cleanRepository();
}
createTargets(scenario);
createDistributionSets(scenario);
if (scenario.isRunRollouts()) {
runRollouts(scenario);
}
}
private void cleanRepository() {
LOGGER.info("Cleaning repository");
deleteTargets();
deleteRollouts();
deleteDistributionSets();
deleteSoftwareModules();
LOGGER.info("Cleaning repository -> Done");
}
private void deleteRollouts() {
// TODO: complete this as soon as rollouts can be deleted
}
private void deleteSoftwareModules() {
PagedList<MgmtSoftwareModule> modules;
do {
modules = softwareModuleResource.getSoftwareModules(0, 100, null, null).getBody();
modules.getContent().forEach(module -> softwareModuleResource.deleteSoftwareModule(module.getModuleId()));
} while (modules.getTotal() > 100);
}
private void deleteDistributionSets() {
PagedList<MgmtDistributionSet> distributionSets;
do {
distributionSets = distributionSetResource.getDistributionSets(0, 100, null, null).getBody();
distributionSets.getContent().forEach(set -> distributionSetResource.deleteDistributionSet(set.getDsId()));
} while (distributionSets.getTotal() > 100);
}
private void deleteTargets() {
PagedList<MgmtTarget> targets;
do {
targets = targetResource.getTargets(0, 100, null, null).getBody();
targets.getContent().forEach(target -> targetResource.deleteTarget(target.getControllerId()));
} while (targets.getTotal() > 100);
}
private void runRollouts(final Scenario scenario) {
distributionSetResource.getDistributionSets(0, scenario.getDistributionSets(), null, null).getBody()
.getContent().forEach(set -> runRollout(set, scenario));
}
private void runRollout(final MgmtDistributionSet set, final Scenario scenario) {
LOGGER.info("Run rollout for set {}", set.getDsId());
// create a Rollout
final MgmtRolloutResponseBody rolloutResponseBody = rolloutResource
.create(new RolloutBuilder().name("Rollout" + set.getName() + set.getVersion())
.groupSize(scenario.getRolloutDeploymentGroups()).targetFilterQuery("name==*")
.distributionSetId(set.getDsId()).successThreshold("80").errorThreshold("5").build())
.getBody();
// start the created Rollout
rolloutResource.start(rolloutResponseBody.getRolloutId(), true);
// wait until rollout is complete
do {
try {
TimeUnit.SECONDS.sleep(35);
} catch (final InterruptedException e) {
LOGGER.warn("Interrupted!");
Thread.currentThread().interrupt();
}
} while (targetResource.getTargets(0, 1, null, "updateStatus==IN_SYNC").getBody().getTotal() < scenario
.getTargets());
LOGGER.info("Run rollout for set {} -> Done", set.getDsId());
}
private void createDistributionSets(final Scenario scenario) {
LOGGER.info("Creating {} distribution sets", scenario.getDistributionSets());
final byte[] artifact = generateArtifact(scenario);
distributionSetResource
@@ -87,6 +169,8 @@ public class ConfigurableScenario {
modules.forEach(module -> assign.id(module.getModuleId()));
distributionSetResource.assignSoftwareModules(dsSet.getDsId(), assign.build());
});
LOGGER.info("Creating {} distribution sets -> Done", scenario.getDistributionSets());
}
private List<MgmtSoftwareModule> addModules(final Scenario scenario, final MgmtDistributionSet dsSet,
@@ -110,8 +194,10 @@ public class ConfigurableScenario {
return modules;
}
private byte[] generateArtifact(final Scenario scenario) {
// create random object
private static byte[] generateArtifact(final Scenario scenario) {
// Exception squid:S2245 - not used for cryptographic function
@SuppressWarnings("squid:S2245")
final Random random = new Random();
// create byte array
@@ -124,14 +210,18 @@ public class ConfigurableScenario {
}
private void createTargets(final Scenario scenario) {
LOGGER.info("Creating {} targets", scenario.getTargets());
for (int i = 0; i < scenario.getTargets() / 100; i++) {
targetResource.createTargets(new TargetBuilder().controllerId(scenario.getTargetName())
.address(scenario.getTargetAddress()).buildAsList(i * 100,
(i + 1) * 100 > scenario.getTargets() ? scenario.getTargets() - i * 100 : 100));
(i + 1) * 100 > scenario.getTargets() ? (scenario.getTargets() - (i * 100)) : 100));
}
LOGGER.info("Creating {} targets -> Done", scenario.getTargets());
}
private int parseSize(final String s) {
private static int parseSize(final String s) {
final String size = s.toUpperCase();
if (size.endsWith("KB")) {
return Integer.valueOf(size.substring(0, size.length() - 2)) * 1024;

View File

@@ -13,12 +13,8 @@ hawkbit.password=admin
spring.main.show-banner=false
#hawkbit.scenarios.[0].targets=0
#hawkbit.scenarios.[0].ds-name=gettingstarted-example
#hawkbit.scenarios.[0].distribution-sets=3
#hawkbit.scenarios.[0].sm-fw-name=gettingstarted-example
#hawkbit.scenarios.[0].sm-sw-name=gettingstarted-example
hawkbit.scenarios.[0].targets=10000
hawkbit.scenarios.[0].distribution-sets=100
hawkbit.scenarios.[0].artifactsPerSM=0
hawkbit.scenarios.[0].targets=0
hawkbit.scenarios.[0].ds-name=gettingstarted-example
hawkbit.scenarios.[0].distribution-sets=3
hawkbit.scenarios.[0].sm-fw-name=gettingstarted-example
hawkbit.scenarios.[0].sm-sw-name=gettingstarted-example