From 8b527cde8f114abea429bc15fe9b87fa9b0cbe11 Mon Sep 17 00:00:00 2001 From: dilshat Date: Wed, 15 Nov 2017 17:32:08 +0600 Subject: [PATCH 1/6] #2119 --- .../modification/steps/SetupP2PStep.java | 6 - .../core/localpeer/impl/LocalPeerImpl.java | 280 +++++++++++++++--- .../impl/entity/ResourceHostEntity.java | 29 +- .../io/subutai/common/peer/LocalPeer.java | 2 - 4 files changed, 250 insertions(+), 67 deletions(-) diff --git a/management/server/core/environment-manager/environment-manager-impl/src/main/java/io/subutai/core/environment/impl/workflow/modification/steps/SetupP2PStep.java b/management/server/core/environment-manager/environment-manager-impl/src/main/java/io/subutai/core/environment/impl/workflow/modification/steps/SetupP2PStep.java index f0533f704d2..b0771be1b2a 100644 --- a/management/server/core/environment-manager/environment-manager-impl/src/main/java/io/subutai/core/environment/impl/workflow/modification/steps/SetupP2PStep.java +++ b/management/server/core/environment-manager/environment-manager-impl/src/main/java/io/subutai/core/environment/impl/workflow/modification/steps/SetupP2PStep.java @@ -114,12 +114,6 @@ private void setupP2p( LocalEnvironment environment, Map> pe if ( !CollectionUtil.isCollectionEmpty( rhIds ) ) { - //remove already participating peers - for ( RhP2pIp rhP2pIp : environment.getP2pIps().getP2pIps() ) - { - rhIds.remove( rhP2pIp.getRhId() ); - } - //assign p2p IPs to new RHs for ( String rhId : rhIds ) { diff --git a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java index fa80f980f5c..0c2344de476 100644 --- a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java +++ b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java @@ -11,12 +11,14 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.security.PermitAll; import javax.annotation.security.RolesAllowed; @@ -221,6 +223,7 @@ public class LocalPeerImpl implements LocalPeer, HostListener, Disposable private transient ScheduledExecutorService cleaner = Executors.newSingleThreadScheduledExecutor(); private transient ExecutorService threadPool = Executors.newCachedThreadPool(); private transient Set peerEventListeners = Sets.newHashSet(); + private AtomicInteger containerCreationCounter = new AtomicInteger(); public LocalPeerImpl( DaoManager daoManager, TemplateManager templateManager, CommandExecutor commandExecutor, @@ -1092,64 +1095,79 @@ public CreateEnvironmentContainersResponse createEnvironmentContainers( { Preconditions.checkNotNull( requestGroup ); - NetworkResource reservedNetworkResource = - getReservedNetworkResources().findByEnvironmentId( requestGroup.getEnvironmentId() ); - - if ( reservedNetworkResource == null ) + try { - throw new PeerException( String.format( "No reserved network resources found for environment %s", - requestGroup.getEnvironmentId() ) ); - } + containerCreationCounter.incrementAndGet(); - Set namesToExclude = Sets.newHashSet(); - for ( ContainerHostInfo containerHostInfo : getNotRegisteredContainers() ) - { - namesToExclude.add( containerHostInfo.getContainerName().toLowerCase() ); - } + NetworkResource reservedNetworkResource = + getReservedNetworkResources().findByEnvironmentId( requestGroup.getEnvironmentId() ); + + if ( reservedNetworkResource == null ) + { + throw new PeerException( String.format( "No reserved network resources found for environment %s", + requestGroup.getEnvironmentId() ) ); + } + Set namesToExclude = Sets.newHashSet(); + for ( ContainerHostInfo containerHostInfo : getNotRegisteredContainers() ) + { + namesToExclude.add( containerHostInfo.getContainerName().toLowerCase() ); + } - //clone containers - HostUtil.Tasks cloneTasks = new HostUtil.Tasks(); - for ( final CloneRequest request : requestGroup.getRequests() ) - { - ResourceHost resourceHost = getResourceHostById( request.getResourceHostId() ); + //clone containers + HostUtil.Tasks cloneTasks = new HostUtil.Tasks(); - CloneContainerTask task = - new CloneContainerTask( request, templateManager.getTemplate( request.getTemplateId() ), - resourceHost, reservedNetworkResource, this, namesToExclude ); + for ( final CloneRequest request : requestGroup.getRequests() ) + { + ResourceHost resourceHost = getResourceHostById( request.getResourceHostId() ); - cloneTasks.addTask( resourceHost, task ); - } + CloneContainerTask task = + new CloneContainerTask( request, templateManager.getTemplate( request.getTemplateId() ), + resourceHost, reservedNetworkResource, this, namesToExclude ); - HostUtil.Results cloneResults = hostUtil.execute( cloneTasks, reservedNetworkResource.getEnvironmentId() ); + cloneTasks.addTask( resourceHost, task ); + } - //register succeeded containers + HostUtil.Results cloneResults = hostUtil.execute( cloneTasks, reservedNetworkResource.getEnvironmentId() ); - for ( HostUtil.Task cloneTask : cloneResults.getTasks().getTasks() ) - { - CloneRequest request = ( ( CloneContainerTask ) cloneTask ).getRequest(); + //register succeeded containers - if ( cloneTask.getTaskState() == HostUtil.Task.TaskState.SUCCEEDED ) + for ( HostUtil.Task cloneTask : cloneResults.getTasks().getTasks() ) { + CloneRequest request = ( ( CloneContainerTask ) cloneTask ).getRequest(); - final HostInterfaces interfaces = new HostInterfaces(); + if ( cloneTask.getTaskState() == HostUtil.Task.TaskState.SUCCEEDED ) + { + + final HostInterfaces interfaces = new HostInterfaces(); - interfaces.addHostInterface( - new HostInterfaceModel( Common.DEFAULT_CONTAINER_INTERFACE, request.getIp().split( "/" )[0] ) ); + interfaces.addHostInterface( new HostInterfaceModel( Common.DEFAULT_CONTAINER_INTERFACE, + request.getIp().split( "/" )[0] ) ); - ContainerHostEntity containerHostEntity = - new ContainerHostEntity( getId(), ( ( CloneContainerTask ) cloneTask ).getResult(), - request.getHostname(), request.getTemplateArch(), interfaces, - request.getContainerName(), request.getTemplateId(), requestGroup.getEnvironmentId(), - requestGroup.getOwnerId(), requestGroup.getInitiatorPeerId(), - request.getContainerQuota() ); + ContainerHostEntity containerHostEntity = + new ContainerHostEntity( getId(), ( ( CloneContainerTask ) cloneTask ).getResult(), + request.getHostname(), request.getTemplateArch(), interfaces, + request.getContainerName(), request.getTemplateId(), + requestGroup.getEnvironmentId(), requestGroup.getOwnerId(), + requestGroup.getInitiatorPeerId(), request.getContainerQuota() ); - registerContainer( request.getResourceHostId(), containerHostEntity ); + registerContainer( request.getResourceHostId(), containerHostEntity ); + } } + + return new CreateEnvironmentContainersResponse( cloneResults ); } + finally + { + containerCreationCounter.decrementAndGet(); + } + } - return new CreateEnvironmentContainersResponse( cloneResults ); + + private boolean isContainerCreationInProgress() + { + return containerCreationCounter.get() > 0; } @@ -1484,7 +1502,6 @@ public void destroyContainer( final ContainerId containerId ) throws PeerExcepti try { resourceHost.destroyContainerHost( host ); - removeQuota( containerId ); } catch ( ResourceHostException e ) { @@ -1505,13 +1522,6 @@ public void destroyContainer( final ContainerId containerId ) throws PeerExcepti } - @Override - public void removeQuota( final ContainerId containerId ) - { - // no-op - } - - @Override public boolean isConnected( final HostId hostId ) { @@ -3786,6 +3796,8 @@ private void removeStaleContainers() continue; } + //remove stale containers (the ones that are registered with Console but not appearing in heartbeats + //from agent) for ( ContainerHost containerHost : resourceHost.getContainerHosts() ) { boolean isContainerEligibleForRemoval = containerHost.getState() == ContainerHostState.UNKNOWN @@ -3814,6 +3826,180 @@ private void removeStaleContainers() LOG.error( e.getMessage() ); } } + + //destroy lost environments (the ones that are present on RH but not registered with Console) + Set lostEnvironmentsVlans = Sets.newHashSet(); + try + { + + Set p2pIfNames = resourceHost.getUsedP2pIfaceNames(); + ReservedNetworkResources reservedNetworkResources = getReservedNetworkResources(); + + for ( String p2pIfName : p2pIfNames ) + { + int vlan = Integer.valueOf( p2pIfName.replace( Common.P2P_INTERFACE_PREFIX, "" ) ); + NetworkResource networkResource = reservedNetworkResources.findByVlan( vlan ); + if ( networkResource == null ) + { + lostEnvironmentsVlans.add( vlan ); + } + } + } + catch ( Exception e ) + { + LOG.error( e.getMessage() ); + } + + for ( Integer vlan : lostEnvironmentsVlans ) + { + try + { + resourceHost.cleanup( null, vlan ); + } + catch ( ResourceHostException e ) + { + LOG.error( e.getMessage() ); + } + } + + + //destroy lost containers (the ones that are present on RH but not registered with Console) + //a) filter out lost ones + Set lostContainers = Sets.newHashSet(); + try + { + ResourceHostInfo resourceHostInfo = hostRegistry.getResourceHostInfoById( resourceHost.getId() ); + + for ( ContainerHostInfo containerHostInfo : resourceHostInfo.getContainers() ) + { + try + { + resourceHost.getContainerHostById( containerHostInfo.getId() ); + } + catch ( HostNotFoundException ignore ) + { + lostContainers.add( containerHostInfo ); + } + } + } + catch ( Exception e ) + { + LOG.error( e.getMessage() ); + } + + //b) ignore manually created containers + for ( Iterator iterator = lostContainers.iterator(); iterator.hasNext(); ) + { + ContainerHostInfo lostContainer = iterator.next(); + + //TODO: use container-name and ip as a workaround for now to figure out environment containers, + //TODO: until env-id is present in container metadata from heartbeat + //filter out containers created by system, not by user + try + { + if ( !( lostContainer.getContainerName().matches( ".*-\\d+-\\d+" ) + + && lostContainer.getHostInterfaces() + + .findByName( Common.DEFAULT_CONTAINER_INTERFACE ).getIp() + .startsWith( "172" ) ) ) + { + iterator.remove(); + } + } + catch ( Exception e ) + { + iterator.remove(); + LOG.error( e.getMessage() ); + } + } + + //skip cleanup in case an environment workflow is in progress since this can lead to inconsistency + if ( isContainerCreationInProgress() ) + { + continue; + } + + //c) destroy lost containers + for ( ContainerHostInfo lostContainer : lostContainers ) + { + try + { + //do additional check here + try + { + resourceHost.getContainerHostById( lostContainer.getId() ); + } + catch ( HostNotFoundException e ) + { + ( ( ResourceHostEntity ) resourceHost ).destroyContainer( lostContainer.getId() ); + } + } + catch ( CommandException e ) + { + LOG.error( e.getMessage() ); + } + } + + + //cleanup empty environments (the ones with no containers on RH) + Set missingNetResources = Sets.newHashSet(); + try + { + //a) filter out net resources missing on this RH + missingNetResources = getReservedNetworkResources().getNetworkResources(); + + for ( Iterator iterator = missingNetResources.iterator(); iterator.hasNext(); ) + { + final NetworkResource networkResource = iterator.next(); + + boolean found = false; + + for ( ContainerHost containerHost : resourceHost.getContainerHosts() ) + { + + if ( Objects.equals( containerHost.getEnvironmentId().getId(), + networkResource.getEnvironmentId() ) ) + { + found = true; + break; + } + } + + if ( found ) + { + iterator.remove(); + } + } + } + catch ( PeerException e ) + { + LOG.error( e.getMessage() ); + } + + + //skip cleanup in case an environment workflow is in progress since this can lead to inconsistency + if ( isContainerCreationInProgress() ) + { + continue; + } + + //b) cleanup empty environments + for ( NetworkResource networkResource : missingNetResources ) + { + //do additional check + if ( resourceHost.getContainerHostsByEnvironmentId( networkResource.getEnvironmentId() ).isEmpty() ) + { + try + { + resourceHost.cleanup( null, networkResource.getVlan() ); + } + catch ( ResourceHostException e ) + { + LOG.error( e.getMessage() ); + } + } + } } } diff --git a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/entity/ResourceHostEntity.java b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/entity/ResourceHostEntity.java index ab24d6218a1..94e64cbdbb6 100644 --- a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/entity/ResourceHostEntity.java +++ b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/entity/ResourceHostEntity.java @@ -453,21 +453,24 @@ public void destroyContainerHost( final ContainerHost containerHost ) throws Res try { - //todo use commandExecutor.execute to avoid exception in case container does not exist - //todo check if exception is due to not existing container and ignore such exception - commandUtil.execute( resourceHostCommands.getDestroyContainerCommand( containerHost.getId() ), this ); + destroyContainer( containerHost.getId() ); } catch ( CommandException e ) { - throw new ResourceHostException( - String.format( "Error destroying container %s: %s", containerHost.getHostname(), e.getMessage() ), - e ); + //silencing error since the container will be removed later by scheduled job + LOG.error( "Error destroying container {}: {}", containerHost.getContainerName(), e.getMessage() ); } removeContainerHost( containerHost ); } + public void destroyContainer( String containerId ) throws CommandException + { + commandUtil.execute( resourceHostCommands.getDestroyContainerCommand( containerId ), this ); + } + + @Override public void setContainerQuota( final ContainerHost containerHost, final ContainerQuota containerQuota ) throws ResourceHostException @@ -647,7 +650,6 @@ public void addContainerHost( ContainerHost host ) @Override public void cleanup( final EnvironmentId environmentId, final int vlan ) throws ResourceHostException { - Preconditions.checkNotNull( environmentId, "Invalid environment id" ); Preconditions .checkArgument( NumUtil.isIntBetween( vlan, Common.MIN_VLAN_ID, Common.MAX_VLAN_ID ), "Invalid vlan" ); try @@ -660,13 +662,16 @@ public void cleanup( final EnvironmentId environmentId, final int vlan ) throws String.format( "Could not cleanup resource host %s: %s", hostname, e.getMessage() ) ); } - Set containerHosts = getContainerHostsByEnvironmentId( environmentId.getId() ); - - if ( !containerHosts.isEmpty() ) + if ( environmentId != null ) { - for ( ContainerHost containerHost : containerHosts ) + Set containerHosts = getContainerHostsByEnvironmentId( environmentId.getId() ); + + if ( !containerHosts.isEmpty() ) { - removeContainerHost( containerHost ); + for ( ContainerHost containerHost : containerHosts ) + { + removeContainerHost( containerHost ); + } } } } diff --git a/management/server/subutai-common/src/main/java/io/subutai/common/peer/LocalPeer.java b/management/server/subutai-common/src/main/java/io/subutai/common/peer/LocalPeer.java index a03cd868ce2..b46485987e1 100644 --- a/management/server/subutai-common/src/main/java/io/subutai/common/peer/LocalPeer.java +++ b/management/server/subutai-common/src/main/java/io/subutai/common/peer/LocalPeer.java @@ -215,8 +215,6 @@ void setVniDomain( Long vni, String domain, ProxyLoadBalanceStrategy proxyLoadBa void setRhHostname( String rhId, String hostname ) throws PeerException; - void removeQuota( ContainerId containerId ); - State getState(); enum State From 683b99ff3bf3a202974e152610bf2a2071e5f160 Mon Sep 17 00:00:00 2001 From: dilshat Date: Wed, 15 Nov 2017 17:58:55 +0600 Subject: [PATCH 2/6] #2119 added check for mgmt container --- .../io/subutai/core/localpeer/impl/LocalPeerImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java index 0c2344de476..ffcf73b0de4 100644 --- a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java +++ b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java @@ -3878,7 +3878,11 @@ private void removeStaleContainers() } catch ( HostNotFoundException ignore ) { - lostContainers.add( containerHostInfo ); + if ( !( resourceHost.isManagementHost() && Common.MANAGEMENT_HOSTNAME + .equalsIgnoreCase( containerHostInfo.getContainerName().trim() ) ) ) + { + lostContainers.add( containerHostInfo ); + } } } } @@ -3943,10 +3947,10 @@ private void removeStaleContainers() //cleanup empty environments (the ones with no containers on RH) + //a) filter out net resources missing on this RH Set missingNetResources = Sets.newHashSet(); try { - //a) filter out net resources missing on this RH missingNetResources = getReservedNetworkResources().getNetworkResources(); for ( Iterator iterator = missingNetResources.iterator(); iterator.hasNext(); ) From 2d0c6cd99333e3728fd056b62f36e7163cda1ccc Mon Sep 17 00:00:00 2001 From: Dilshat Date: Thu, 16 Nov 2017 12:38:53 +0600 Subject: [PATCH 3/6] added logs --- .../java/io/subutai/core/localpeer/impl/LocalPeerImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java index ffcf73b0de4..5991c59407b 100644 --- a/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java +++ b/management/server/core/local-peer/local-peer-impl/src/main/java/io/subutai/core/localpeer/impl/LocalPeerImpl.java @@ -3854,6 +3854,8 @@ private void removeStaleContainers() { try { + LOG.warn( "Removing lost environment, vlan = {}", vlan ); + resourceHost.cleanup( null, vlan ); } catch ( ResourceHostException e ) @@ -3936,6 +3938,8 @@ private void removeStaleContainers() } catch ( HostNotFoundException e ) { + LOG.warn( "Removing lost container {}", lostContainer.getContainerName() ); + ( ( ResourceHostEntity ) resourceHost ).destroyContainer( lostContainer.getId() ); } } @@ -3996,6 +4000,8 @@ private void removeStaleContainers() { try { + LOG.warn( "Removing empty environment, vlan = {}", networkResource.getVlan() ); + resourceHost.cleanup( null, networkResource.getVlan() ); } catch ( ResourceHostException e ) From c74f00976bce97e1f525fd3c72699f293f6b2109 Mon Sep 17 00:00:00 2001 From: dilshat Date: Fri, 17 Nov 2017 11:22:06 +0600 Subject: [PATCH 4/6] fixes Public URL IP detection --- .../core/hubmanager/impl/HubManagerImpl.java | 18 ++++++++++++++++++ .../core/peer/impl/PeerManagerImpl.java | 7 +++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/management/server/core/hub-manager/hub-manager-impl/src/main/java/io/subutai/core/hubmanager/impl/HubManagerImpl.java b/management/server/core/hub-manager/hub-manager-impl/src/main/java/io/subutai/core/hubmanager/impl/HubManagerImpl.java index 88b6ed5869c..d515c82e623 100644 --- a/management/server/core/hub-manager/hub-manager-impl/src/main/java/io/subutai/core/hubmanager/impl/HubManagerImpl.java +++ b/management/server/core/hub-manager/hub-manager-impl/src/main/java/io/subutai/core/hubmanager/impl/HubManagerImpl.java @@ -713,8 +713,26 @@ public void removeListener( HubEventListener listener ) public void destroy() { heartbeatExecutorService.shutdown(); + peerLogsExecutor.shutdown(); + resourceHostMonitorExecutorService.shutdown(); + + containersMetricsExecutorService.shutdown(); + + asyncHeartbeatExecutor.shutdown(); + + peerMetricsExecutorService.shutdown(); + + hubLoggerExecutorService.shutdown(); + + containerEventExecutor.shutdown(); + + environmentTelemetryService.shutdown(); + + versionEventExecutor.shutdown(); + + tunnelEventService.shutdown(); } diff --git a/management/server/core/peer-manager/peer-manager-impl/src/main/java/io/subutai/core/peer/impl/PeerManagerImpl.java b/management/server/core/peer-manager/peer-manager-impl/src/main/java/io/subutai/core/peer/impl/PeerManagerImpl.java index 7d645faeccf..7e070cfcc93 100644 --- a/management/server/core/peer-manager/peer-manager-impl/src/main/java/io/subutai/core/peer/impl/PeerManagerImpl.java +++ b/management/server/core/peer-manager/peer-manager-impl/src/main/java/io/subutai/core/peer/impl/PeerManagerImpl.java @@ -175,6 +175,8 @@ public void init() public void destroy() { commandResponseListener.dispose(); + + localIpSetter.shutdown(); } @@ -1556,10 +1558,7 @@ public void run() ( ( ResourceHostInfo ) hostRegistry.getHostInfoById( localPeer.getManagementHost().getId() ) ) .getAddress(); - if ( setLocalPeerUrl( ip ) ) - { - localIpSetter.shutdown(); - } + setLocalPeerUrl( ip ); } catch ( Exception e ) { From b5be0b9c273307b0b7168942f2bf42cd9015dc1f Mon Sep 17 00:00:00 2001 From: dilshat Date: Fri, 17 Nov 2017 11:31:27 +0600 Subject: [PATCH 5/6] show "N/a" if Os name is not determined --- .../webui/src/main/webapp/subutai-app/about/partials/about.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html b/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html index 869c2cbc590..f208fedb444 100755 --- a/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html +++ b/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html @@ -44,7 +44,7 @@ RH OS - {{ aboutCtrl.info.osName }} + {{ aboutCtrl.info.osName ? aboutCtrl.info.osName : "N/a" }} P2P Version From 8a15a0e741b81b8d3b5db0d53c8d5064fc9c0904 Mon Sep 17 00:00:00 2001 From: dilshat Date: Fri, 17 Nov 2017 12:07:19 +0600 Subject: [PATCH 6/6] show "N/A" if Os name is not determined --- .../webui/src/main/webapp/subutai-app/about/partials/about.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html b/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html index f208fedb444..94a4eeb4a2e 100755 --- a/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html +++ b/management/server/webui/src/main/webapp/subutai-app/about/partials/about.html @@ -44,7 +44,7 @@ RH OS - {{ aboutCtrl.info.osName ? aboutCtrl.info.osName : "N/a" }} + {{ aboutCtrl.info.osName ? aboutCtrl.info.osName : "N/A" }} P2P Version