Startup and Ingestion Lvl 1

  •   8:45 – 10:45 Architectural Overview & Predix Tooling
  • 10:45 – 11:00 Break
  • 11:00 – 12:45 Machine Setup (Dev environment)
  • 12:45 – 13:45 Lunch Break
  • 13:45 – 14:45 Security Model & UAA
  • 14:45 – 15:00 Break
  • 15:00 – 17:00 Time Series
"The cloud is not about where computing is done, it’s about how computing is done.”

Paul Maritz!
Executive Chairman, Pivotal!

Predix Architecture Overview

Why such choice !

Cloud Foundry is a bit ironic

it is opinionated
It allows you to organize, simplify and lock down your IT infrastructure

It is open and flexible
  • It works on multiple infrastructures (Amazon Web Services, vSphere, OpenStack...)
  • It works in conjunction with your existing applications, tools and services
  • It works with many languages and frameworks

CEO Perspective- Focus on the Business Domain


What is Cloud Foundry from a CEO’s perspective?


Google, Facebook, Twitter, Netflix, etc. have thousands of engineers working on their unique, consumer-grade platforms


Cloud Foundry encapsulates industry best practices in a platform, allowing enterprises to focus on their business domain

CEO Perspective- Transformational


Cloud Foundry is transformational in two ways:


  1. Business- it facilitates the change to agile, cloud-native software development processes

  2. Technology- enables a simplified, efficient, scalable infrastructure
Predix CF Components - 1.4
Predix CF Components - Diego
Load Balancer (HA Proxy)

Description:

  • HAProxy is separate component from the Router
    • Sits in front of the router
    • By default only one instance defined
  • Cloud Foundry can only have one IP address visible to outside world
    • This is the Load Balancer
    • It passes requests to the Router(s) to send to the right CF component

Responsibilities:

  • Load-balancing across multiple routers (if used)
  • SSL Termination

  • Note: Typically need to configure your own HAProxy (in an HA configuration) or other load- balancer (such as F5 or NSX) for improved performance/SSL handling
(Go) Router

Description:

  • Routes all incoming HTTP traffic
    • System traffic (cf commands)
    • Application traffic
  • Maintains dynamic routing table for each load-balanced app instance
    • Knows IP addresses and ports.
  • Multiple routers possible
    • Configured by admin in Ops Manager
    • Rewritten in Go since CF 1.4 – the GoRouter

Responsibilities:

  • Load balancing across application instances
  • Maintaining an active routing table
  • Access logs
  • Supports web-sockets
UAA and Login Services

Description:

  • UAA = “User Account and Authentication”
  • Provides identity, security and authorization services.
  • Manages third party OAuth 2.0 access credentials
  • Can provide application access & identity-as-a-service for CF apps
  • Composed of
    • UAA Server
    • Command Line Interface (UAAC)
    • Library.
  • Multiple UAA/Login Servers possible

Responsibilities:

  • Token Server
  • ID Server (User management)
  • OAuth Scopes (Groups) and SCIM (System for Cross-domain Identity Management)
  • Login Server
    • UAA Database
    • SAML support (for SSO integration) and Active Directory support with the VMWare SSO Appliance
  • Access auditing
Cloud Controller (CC)

Description:

  • Command and Control
    • Responds to clients (CLI, App Manager, IDEs)
    • Account and provisioning control.
  • Provides RESTful interface to domain objects
    • Apps, services, organizations, spaces, service instances, user roles, and more ...
  • Multiple Cloud Controllers possible
    • Configurable by Admin

Responsibilities:

  • Expected Application state
  • State transitions
  • Permissions/Authorization
  • Organizations/Spaces/Users
  • Services management
  • Auditing/Journaling and billing events
  • Blob Storage
Cloud Controller Database (CCDB)

Description:

  • Storage for application metadata
  • Used exclusively by the Cloud Controller

Responsibilities:

  • Stores information like
    • Application Name
    • # of instances requested
    • Memory Limits
    • Routes
    • Bound Services
  • A Postgres DB instance
Droplet Execution Agents / Cells

Description:

  • Droplet Execution Agents (DEA's), renamed “Cells” in Diego
  • Secure and fully isolated containers
    • A Virtual Machine (VM)
  • Periodically broadcast messages about their state
  • Typically many DEA's/cells in a Cloud Foundry installation

Responsibilities:

  • Managing app containers
  • Monitoring resource pools
    • Process, file system, network, memory
  • Managing app lifecycle - starting and stopping apps as instructed
  • App log and file streaming
  • Sending heartbeats for health monitoring
Warden / Garden

Description:

  • Isolated Process
    • Safe, lightweight alternative to full VM
  • Allows multiple applications to run on each VM
  • Garden is Warden rewritten in Go

Responsibilities:

  • Isolates applications running on the same VM
    • Individual failures does not affect other applications on the VM
    • Uses kernel namespaces to isolate network, disk, memory and CPU
    • Uses Linux cgroups to do resource management
  • Secures applications from environment
  • Runs Droplets
Where do I work - Organizations & Spaces
  • The Org is the top-most administrative unit
  • Any Cloud Foundry installation can have multiple organizations defined in many way
    • Typically a company, department, application suite or large project
  • Designed to support many users working collaboratively

  • Organizations contains multiple spaces
  • Spaces also can be defined in many way
  • For example, staging and production

  • Applications and services are bounded to a space (tricks around that)
  • Provides a set of users access for:
    • Application development
    • Functionality and/or performance testing – Quality assurance
    • Deployment to production
    • Maintenance
Break
Predix Dev Tools - Virtual Box Install

Windows Machine:

  • If you have a GE laptop the antivirus will block you, you need to install an old version of virtual box: Virtual Box

  • Otherwise you could use the latest and greatest Virtual Box

OSX

For OSX the easiest way to install virtual box is to use brew
brew cask install virtualbox \ virtualbox-extension-pack
Predix Dev Tools - DevBox Install
  • Import the Ova into Virtual Box
The output should look like this : The login/password is predix/predix
Predix Dev Tools - DevBox Network Setup - GE Employee only
  1. Login to the virtual machine
  2. Open chrome browser go to settings and in proxy set the automatic mode with : http://myapps.setpac.ge.com/pac.pac
  3. Check that you can have acces to https://predix.io
Predix Dev Tools - DevBox Network Setup - Non GE Employee
  • If you are on a non proxied network you need to remove the proxy from the terminal
    • Open a terminal and type : sudo gedit /etc/bashrc
    • Go to the botton of the file and comments
      export http_proxy=http://sjc1intproxy01.crd.ge.com:8080/
      export https_proxy=$http_proxy
      export HTTP_PROXY=$http_proxy
      export HTTPS_PROXY=$http_proxy
      export no_proxy="127.0.0.1,localhost,localhost.localdomain,*.ge.com"
  • Logout of the VM and Login again
  • Open a terminal and check that the following command result is empty env | grep http_proxy
  • Try to download google index wget https://www.google.com
  • Check that you have something cat index.html
Predix Dev Tools - DevBox Maven Setup - GE & Non Employee
  • On the devbox open ~/.m2/settings.xml
  • Search for this :
    <server>
    <id>artifactory.external</id>
    <username>your.predix.io.username</username>
    <password>your.encrypted.password</password>
    <!-- Obtained from https://artifactory.predix.io -->
    </server>
  • Replace username by your predix.io login (email address)
  • For the password get it from artifactory
    • Go to : https://artifactory.predix.io
    • Login using you predix.io login and password
    • Click on your name (up right)
    • enter your predix.io password
    • Click on the eye icon om presonnal settings to get your key
    • Copy this key in place of the password in your ~/.m2/settings.xml
Predix Dev Tools - DevBox Maven Setup - Non GE Employee
  • On the devbox open ~/.m2/settings.xml
  • Search for proxies:
    
    

    <proxies>
    <proxy>
    <id>corporate</id>
    <active>true</active>
    <protocol>http</protocol>
    <host>sjc1intproxy01.crd.ge.com</host>
    <port>8080</port>
    <username>proxyuser</username>
    <password>proxypass</password>
    <nonProxyHosts>127.0.0.1,localhost,localhost.localdomain,*.ge.com</nonProxyHosts>
    </proxy>
    </proxies>

  • Comment the whole block
Predix Dev Tools - Maven Setup
  • If you are on a mac or on linux copy this file settings.xml to ~/.m2/settings.xml
  • Redo the same as previously
    • With or without the proxy depending of your network
Predix Dev Tools - Java Language
  • The Devbox comes with STS Tool suite (eclipse based IDE)
  • JDK version is 1.8.0_151
  • Maven version 3.3.9

  • During this training we will use only the devbox for simplicity sake
  • Using OSX or Linux is more efficient for heavy duty development
Predix Dev Tools - Non Java Languages
  • For non Java Languages development the devbox include Atom Editor which more than sufficient for the scope of this training
  • For serious development investing into a Intellij Ultimate License might be a good idea

  • Node version: 6.3.0
  • Npm version: 3.10.3
  • Python version: 2.7.5
  • Python version: 3.4.3
  • gcc version: 4.8.5
Predix Dev Tools - Predix cli
  • A simple, text-based Predix client
  • Used by operators and developers to access and work with any Predix installation
  • Anything you do with the CLI can be scripted/automated

  • Already install on the devbox
  • see: https://github.com/PredixDev/predix-cli
  • Go for the manual install the one liner is broken
  • See https://github.com/PredixDev/predix-cli/wiki for more info
Predix Dev Tools - Cf cli primer
  • Getting some help
    • cf --help
    • cf help {command}
  • Login to Cf
    • cf login -a https://api.system.aws-usw02-pr.ice.predix.io
    • Enter your login and password
  • Set your Org and Space
    • cf target -o {Orgname} -s {SpaceName}
  • List services available
    • cf marketplace
  • List running application
    • cf apps
  • List running services
    • cf services
  • Deploy an application
    • cf push -f manifest.yml
    • The manifest file describes what kind application you have and all its service dependencies
Predix Dev Tools - Predix Cf cli primer suite
  • Get log of your app
    • predix logs {AppName}
    • Only latest ones predix logs {AppName} --recent
  • Delete an app
    • predix cf delete {AppName}
  • Get Environment of a running application
    • predix cf env {AppName}
  • List running services
    • predix cf services
  • Get info of a given app
    • predix cf app {AppName}
Predix Dev Tools - The .cf Directory
  • cf creates a .cf directory in your home directory
  • Stores context, logs, crash reports ...
  • Remembers your CF API endpoint
    • Don't need to specify -a option at next login
Lab - Few predix cli usage
  • Set your org
  • Create a space named lab
  • Set target to lab
  • Create a space named lab2
  • Set target to lab2
  • Set target to lab
  • Delete space named lab2
Lab - Few cf cli usage
  • predix cf target -o "Email"
  • predix cf create-space lab
  • predix cf target -s "lab"
  • predix cf create-space lab2
  • predix cf target -s "lab2"
  • predix cf target -s "lab"
  • predix cf delete-space "lab2"
  • predix cf target -s "lab"
Lab - The first app

mkdir nullApp && cd nullApp
touch Procfile
predix cf push nullApp -b http://github.com/ryandotsmith/null-buildpack.git --no-start --no-route -m 32m -k 64m
                    

  • Using predix cli try to :
    • Check the logs
    • Try to scale it to two instances
    • Try to restart the application
    • Delete the application
Break
Predix Security Model - Architecture intro
  • All inbound Communications are using ssl :
    • https:// is the default and must be used as default prefix in the browser
    • For web socket wss is used
  • No direct tcp inbound : mqtt is not possible
  • Only TLS 1.2 is possible some iot devices not supported
  • Authentication is done using OAuth2.0

  • In CF this role is taken by the UAA service (java service)
    • Stores user account
    • Stores client
    • Validate token
Predix Security Model - UAA intro
  • We must differentiate two kind of usages :
    • Resource access:
    • User login:
Predix Security Model - UAA endpoints
  • Obtain access token at /oauth/token
  • Inspect access token at /check_token
  • Obtain token key (for decoding JWT tokens locally) at /token_key
  • Login ui to access application /login
  • Authorization Requests Code /oauth/authorize
  • Validate an access token /check_token
  • Given an appropriate access_token,
    returns information about a user /userinfo
Predix Security Model - OAuth2
  • Resource Access is very suited for stateless services (without any session)
    • Only Client(client_id/client_secret) is needed
  • User login required a session storage (using redis most of the time)
    • Require a User Account (login/password) in UAA, a Client (client_id/client_secret and a Callback setup

  • Each time a token is generated we must pay attention to:
    • it's expiration date
    • it's validation
  • If the token expire it's needs to be refreshed
  • To validate the token a call to UAA service is needed (extra latency): Fast token to the rescue
    • it's a library that fetch the UAA token signing key at startup and perform the validation locally.
Predix Security Tools - uaac cli
  • A simple, text-based Cloud foundry uaa client
  • Used by operators and developers to access and work with uaa service instances
  • Anything you do with the uua cli can be scripted/automated
uaac cli - primer
  • Get or Set target uaa
    • predix uaa target {uaaUri}
  • Create uaa client
    • predix uaa client create {client_id} -s {client_secret}
  • Update uaa client
    • predix uaa client update {client_id} {Additional properties}
  • Create a group
    • predix uaa group add {groupname}
Lab - UAA
  • Using the predix.io console create an UAA service in the space lab
    • Space: lab
    • Instance name: uaa-lab
    • Use the Free Plan
    • Please keep the password
    • Use lab-{your initials} as subdomains
  • Select space lab and click on uaa-lab service, login using the password previously saved
  • Add a new client lab-client using the Service instance ui with the following parameters
    • Client Id:rest-client
    • Grant type: client_credentials
    • Client Secret: {PICK ONE}
    • Scope: uaa.none
    • Authorities: uaa.none
    • Allowed Providers: uaa
Lab - UAA Automation
We said that anything done with cf and uaac cli can be automated...

                        predix create-service predix-uaa Free uaa-lab -a {UAA_SECRET} -C '{"subdomain":"lab-{your initials}"}'
                    


                            predix uaa client create rest-client -s "{CLIENT_SECRET}" --authorized_grant_types "client_credentials" --scope "uaa.none" --authorities "uaa.none"
                    
Lab - Explanation
  • Grant type:
    • An access token can be requested in four different ways, in the Oauth specification they are referred to as grant types:
      • client_credentials - no user involved. requesting a token to represent a client only
      • password - the client uses the user's credentials and passes them to the UAA to generate a token.
      • implicit - this is similar to the password grant, but a client password(secret) is not needed
      • authorization_code - in this scenario, the client never sees the user's credentials. It is the most secure grant type but relies on 302 redirects from the HTTP protocol.
  • Scope:
    • Scopes are essentially permissions, and are added as a named parameter in the access token.
      • scope - when the token represents a client acting on behalf of a user
      • authorities - when the token represents the client (application) itself
Lab - Resource access Console
  • In the devbox install postman its a chrome app that allow you to call any rest api
  • Enter the url of the uaa service
    https://lab-{your initials}.predix-uaa.run.aws-usw02-pr.ice.predix.io plus add the extension to obtain a token
  • Send a basic authentication POST request to get a JSON web token for the client rest-client
  • You should have something like:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiI5MzM3Yzc5MzE5MmE0MDcwYjA5ZmQ2MzdmZDZkMzY5MSIsInN1YiI6InJlc3RfY2xpZW50Iiwic2NvcGUiOlsidWFhLm5vbmUiXSwiY2xpZW50X2lkIjoicmVzdF9jbGllbnQiLCJjaWQiOiJyZXN0X2NsaWVudCIsImF6cCI6InJlc3RfY2xpZW50IiwiZ3JhbnRfdHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyIsInJldl9zaWciOiIzMDBkYmJkOCIsImlhdCI6MTQ4MTEyODIwMSwiZXhwIjoxNDgxMTcxNDAxLCJpc3MiOiJodHRwczovL2xhYi5wcmVkaXgtdWFhLnJ1bi5hd3MtdXN3MDItcHIuaWNlLnByZWRpeC5pby9vYXV0aC90b2tlbiIsInppZCI6IjIyMTQ0NmQyLTNiNWUtNDAyYy1hNmQ1LTZhYWVjZTIyZDQzYiIsImF1ZCI6WyJyZXN0X2NsaWVudCJdfQ.h3QEK1hP5pgWWpYRoqnthKhh7_FbhKXSs_7tbEcOvqB59E46zFDC4btiBJjN7k6D1waTkuHaXiXaUxpnijwDamsHl7qfayHBkX5tDie7YQL9v4n49AJpLkA-sONDbdjr0TdbtlLYxqABInHoWEQgWEHhMBWrZDfFCiIneRWNCvwAS_d84bm2YeoodlLBG8tfo3IxxDOaB_HD0kaxeR5X54lvoahXC31m3STsxqbZSsODTggIPgphKfnQH4oSmVePDpA1LV51J0r32EhPXvXEUOSkzad3wlr365Hp7Gb36J3i-d4yfbRqLSreMmI7h2AFbBy4M5YhKHEt_DlzWnOJBw","token_type": "bearer",
    "expires_in": 43199,
    "scope": "uaa.none",
    "jti": "9337c793192a4070b09fd637fd6d3691"
}
                    
    Hints:
    • You need a Basic Auth Header (Postman can generate it for you) containing {client_id}:{client_secret}
    • The body of the request need to include the grant_type
    • Use x-www-form-urlencoded
Lab - Resource access Console
  • Enter the url of the uaa service
    https://lab-{your initials}.predix-uaa.run.aws-usw02-pr.ice.predix.io plus add the extension to check a token
  • Send a basic authentication POST request to check the bearer token rest-client
  • You should have something like:

{"error":"access_denied","error_description":"Access is denied"}
                    
    Hints:
    • You need a Basic Auth Header (Postman can generate it for you) containing {client_id}:{client_secret}
    • The body of the request need to include the token with the token you got from the previous call.
    • Use x-www-form-urlencoded
Lab - Resource access Console
  • To check the token the client needs to have uaa.resource in its Authorities
  • Using the UAA dashboard edit the client to update the client
  • ReSend the previous request request
  • You should have something like:

                        {
  "client_id": "rest-client",
  "exp": 1481172828,
  "scope": [
    "uaa.none"
  ],
  "jti": "d8fd8a1cc0714bfd97870ff50f677034",
  "aud": [
    "rest-client"
  ],
  "sub": "rest-client",
  "iss": "https://lab-{your initials}.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token",
  "iat": 1481129628,
  "cid": "rest-client",
  "grant_type": "client_credentials",
  "azp": "rest-client",
  "zid": "221446d2-3b5e-402c-a6d5-6aaece22d43b",
  "rev_sig": "300dbbd8",
  "revocable": false
}
                    
Lab - Resource access Predix cli
  • To check the token the client needs to have uaa.resource in its Authorities
  • Using the predix uaa cli to update the client
  • ReSend the previous request request
  • You should have something like:

                        {
  "client_id": "rest-client",
  "exp": 1481172828,
  "scope": [
    "uaa.none"
  ],
  "jti": "d8fd8a1cc0714bfd97870ff50f677034",
  "aud": [
    "rest-client"
  ],
  "sub": "rest-client",
  "iss": "https://lab-{your initials}.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token",
  "iat": 1481129628,
  "cid": "rest-client",
  "grant_type": "client_credentials",
  "azp": "rest-client",
  "zid": "221446d2-3b5e-402c-a6d5-6aaece22d43b",
  "rev_sig": "300dbbd8",
  "revocable": false
}
                    
Lab - Resource access Cli Solution

predix uaa login lab-{your initials}
predix uaa client update rest-client --authorities "uaa.none uaa.resource"
                        
Lab - UAA Login Console
  • Using the UAA dashboard create a new client name login_client with the following properties:
    • Client Id:login_client
    • Grant type: authorization_code
    • Client Secret: {client_secret}
    • Scope: uaa.none
    • Authorities: uaa.none and uaa.user
    • Allowed Providers: uaa
    • Redirect URL: https://www.getpostman.com/oauth2/callback
  • Create a User with the following properties:
    • User Name: {Pick one email}
    • Email: {Pick one email}
    • Password: {Pick one}
    • Given Name: {Pick One}
    • Active: check it
    • Verified: check it
Lab - UAA Login CLI Solution

predix uaa login lab-{your initials}
predix uaa client create login_client -s {SECRET} --authorized_grant_types "authorization_code" --scope "uaa.none uaa.user" --authorities "uaa.none" --redirect_uri "https://www.getpostman.com/oauth2/callback"
predix uaa user create {Pick One} --emails "{Pick One}" --given_name "{Pick One}" -p "{Pick One}"
Be carefull with the password format :

                    Invalid status response: 400. Password must be at least 8 characters in length. Password must contain at least 1 special characters. Password must contain at least 2 digit characters. Password must contain at least 2 uppercase characters.
                    
Lab - UAA Login GUI
  • In postman open Authorization Helper and select OAuth2.0 and enter :
    • Token Name: Bearer
    • Callback URL: https://www.getpostman.com/oauth2/callback
    • Auth URL: https://lab-{your initials}.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/authorize
    • Access Token URL: https://lab-{your initials}.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token
    • Client ID: login_client
    • Client Secret: {client_secret}
    • Grant Type: Authorization Code
  • Press Request Token
  • At the end you will have a Bearer token registered that could be added to any request
  • What can be added to remove the approval step ?
  • predix uaa client update login_client --autoapprove "uaa.user"
Security - Multi Language
Lab - Securing Application with Java - Devbox
  • Clone the git repository https://github.com/BLaurent/predix-starter
  • Go to the directory security-demo
  • Compile it and deploy
    
    mvn clean package
    predix cf push
                            
  • Check that the application is working by opening a browser and checking that /dashboard?user=toto
  • This is important to prefix things with https
Lab - Securing application with Java - Devbox
  • Add in the pom
    
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
                                
  • 
    mvn clean package
    predix cf push
                            
  • Open the same URL as before, you should have a prompt for a basic auth login
Lab - Securing application with Java - Devbox
  • We need to enable the User auth mechanism
  • Add the following annotation @EnableOAuth2Sso after the @Log annotation in the class SecurityDemoApplication
  • Open the application.yml file and uncomment all lines
  • Change all things that fit your setup: clientId, clientSecret, accessTokenUri, userAuthorizationUri, userInfoUri
  • This should look like that
    
    security:
      oauth2:
        client:
          clientId: login_client
          clientSecret: secret
          accessTokenUri: https://lab-demo.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token
          userAuthorizationUri: https://lab-demo.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/authorize
          tokenName: oauth_token
        resource:
          userInfoUri: https://lab-demo.predix-uaa.run.aws-usw02-pr.ice.predix.io/userinfo
    spring:
      resources:
        chain:
          enabled: true
    logging:
      level:
        org:
          springframework:
            security: DEBUG
                                
Lab - Securing application with Java - Devbox
  • Redo the deployment and open the url as in the previous step
  • You now have a login page: enter the login and password of the user you've created
  • You should should have an error : Invalid redirect
  • We didn't setup the url redirection in the token as we did for postman. With spring the URL is by default: http://{Your App URL}/login
  • Either with the web console or with the Cli update the login_client to enable the redirection

predix uaa client update login_client --authorized_grant_types "authorization_code" --scope "uaa.none uaa.user" --authorities "uaa.none" --redirect_uri "https://www.getpostman.com/oauth2/callback, http://security-demo-noisy-numbat.run.aws-usw02-pr.ice.predix.io/login"
                        
Lab - Securing application with Java - Devbox
  • Redo the deployment and open the url as in the previous step
  • You now have a login page: enter the login and password of the user you've created
  • You should should have an error : Authentication Failed: Could not obtain user details from token
  • We didn't enable the sso login on the client to fetch userinfo from uaa UAA SSO, UAA user info
  • Either with the web console or with the Cli update the login_client to enable the redirection

predix uaa client update login_client --authorized_grant_types "authorization_code" --scope "uaa.none uaa.user openid" --authorities "uaa.none" --redirect_uri "https://www.getpostman.com/oauth2/callback, https://security-demo-noisy-numbat.run.aws-usw02-pr.ice.predix.io"
                        
Securing application with Java - Summary
  • We seen how to use @EnableOAuth2Sso to manage user login
    • Your web server is in Java maybe have a look to JHipster
    • You do not known Node.js (Predix web server is written in Node.js)
  • What happen if we want to secure services with only not user facing rest endpoints
Securing REST endpoints with Java - Devbox
  • Go into the simple-predix-service directory this directory will serve as a base for many of our experiments
  • The heart of this feature is a Single Java Class, an annotation and GE package:
    • The @EnableResourceServer annotation configure our endpoints to be secured
    • Class SecurityConfig extends ResourceServerConfigurerAdapter complex rules
    • uaa-token-lib GE package allow you to implement fast token with UAA see fast token slide
Lab - Securing REST endpoints with Java - Devbox
  • Update the manifest.yml file to match your previous configuration
  • You need to enable the proper profile SPRING_PROFILES_ACTIVE: security add it to env
  • Add the @EnableResourceServer annotation under the @SpringBootApplication annotation
  • Deploy this application
  • Using your browser try to reach the URL : https://Your App/dashboard?user=toto
  • Open Postman and try to generate a request including the token to make it work
Lab - Securing REST endpoints with Python - Devbox
Securing Rest endpoint is a more manual to setup
  • Clone the git repository https://github.com/BLaurent/predix-starter
  • We use Flask package as server with python 3.6
  • Deploying application is done using Predix CLI
  • go to the simple-predix-service-python directory

predix cf push
                    
Lab - Securing REST endpoints with Python - Devbox
  • To run it locally

pip install pipenv
pipenv install --three
pipenv shell
python app.py runserver
                    
  • Open a new terminal

curl http://127.0.0.1:8080/api/v1/drones
"Hello World!"
                    
  • Uncomment the line containing @app.before_request
  • Deploy the application
    
    predix cf push
                        
  • Open Your browser and check that the security in enabled
    
    https://simple-predix-service-python-xxx-yyy.run.aws-usw02-pr.ice.predix.io/api/v1/drones
                                
  • This shall gave you an error:
    
    {
    message: "The server could not verify that you are authorized to access the URL requested.
                                    You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required."
    }
                                
Securing REST endpoints with Python
  • We rely on Flask decorator before_request to execute the token validation before processing the request
  • Each rest end point is describe as a blueprint class in the drones package
  • Predix python SDK to simplify bootstrap and the integration Predix
  • Fast Token is not implemented yet, the download of the key is needed
  • Some dependencies have been updated for this training to support py3 see
Anatomy of an application Devbox
How do we depoy an App ?
  • Cloud Foundry support many languages: Java, Node.js, Python, Php...
  • Some property of the application are in a manifest.yml file:
    
    ---
    applications:
      - name: simple-predix-service
        random-route: true
        memory: 512M
        disk_quota: 512M
        instances: 1
        buildpack: java-buildpack
        path: target/simple-predix-service-0.0.1-SNAPSHOT.jar
        services:
          - uaa-lab
        env:
          uaaClientId: rest-client
          uaaClientSecret: rest-client
          SPRING_PROFILES_ACTIVE: security
                            
  • Source code is uploaded, then packaged into a container
  • This packaging is done using a buildpack: The recipe
    • Dependencies: pip install, npm install, ...
    • Handling of the routing
    • Allocation of resources
    • ...
  • Example of cf push output:
    
    Pushing from manifest to org European_Foundry_Basic / space machine as Benoit.Laurent@ge.com...
    Using manifest file /Users/benoitlaurent/Git/predix-starter/simple-predix-service/manifest.yml
    Getting app info...
    Creating app with these attributes...
    + name:         simple-predix-service
      path:         /Users/benoitlaurent/Git/predix-starter/simple-predix-service/target/simple-predix-service-0.0.1-SNAPSHOT.jar
    + buildpack:    java-buildpack
    + disk quota:   512M
    + instances:    1
    + memory:       512M
      services:
    +   uaa-lab
      env:
    +   SPRING_PROFILES_ACTIVE
    +   uaaClientId
    +   uaaClientSecret
      routes:
    +   simple-predix-service-quiet-parrot.run.aws-usw02-pr.ice.predix.io
    
    Creating app simple-predix-service...
    Mapping routes...
    Binding services...
    Comparing local files to remote cache...
    Packaging files to upload...
    Uploading files...
     1.29 MiB / 1.29 MiB [========================================================================================================================================================================] 100.00% 2s
    
    Waiting for API to complete processing files...
    
    Staging app and tracing logs...
       -----> Downloaded app package (51M)
       -----> Java Buildpack Version: v3.13 | https://github.com/cloudfoundry/java-buildpack.git#03b493f
       -----> Downloading Open Jdk JRE 1.8.0_121 from https://java-buildpack.cloudfoundry.org/openjdk/trusty/x86_64/openjdk-1.8.0_121.tar.gz (0.8s)
              Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (1.0s)
       -----> Downloading Open JDK Like Memory Calculator 2.0.2_RELEASE from https://java-buildpack.cloudfoundry.org/memory-calculator/trusty/x86_64/memory-calculator-2.0.2_RELEASE.tar.gz (0.0s)
              Memory Settings: -Xms317161K -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M -Xss228K -Xmx317161K
       -----> Downloading Container Certificate Trust Store 2.1.0_RELEASE from https://java-buildpack.cloudfoundry.org/container-certificate-trust-store/container-certificate-trust-store-2.1.0_RELEASE.jar (0.0s)
              Adding certificates to .java-buildpack/container_certificate_trust_store/truststore.jks (0.3s)
       -----> Downloading Spring Auto Reconfiguration 1.12.0_RELEASE from https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-1.12.0_RELEASE.jar (0.0s)
    
       -----> Uploading droplet (97M)
    
    Waiting for app to start...
    
    name:              simple-predix-service
    requested state:   started
    instances:         1/1
    usage:             512M x 1 instances
    routes:            simple-predix-service-quiet-parrot.run.aws-usw02-pr.ice.predix.io
    last uploaded:     Tue 23 Jan 12:40:19 CET 2018
    stack:             cflinuxfs2
    buildpack:         java-buildpack
    start command:     CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_jre/bin/java-buildpack-memory-calculator-2.0.2_RELEASE -memorySizes=metaspace:64m..,stack:228k..
                       -memoryWeights=heap:65,metaspace:10,native:15,stack:10 -memoryInitials=heap:100%,metaspace:100% -stackThreads=300 -totMemory=$MEMORY_LIMIT) && JAVA_OPTS="-Djava.io.tmpdir=$TMPDIR
                       -XX:OnOutOfMemoryError=$PWD/.java-buildpack/open_jdk_jre/bin/killjava.sh $CALCULATED_MEMORY
                       -Djavax.net.ssl.trustStore=$PWD/.java-buildpack/container_certificate_trust_store/truststore.jks -Djavax.net.ssl.trustStorePassword=java-buildpack-trust-store-password" &&
                       SERVER_PORT=$PORT eval exec $PWD/.java-buildpack/open_jdk_jre/bin/java $JAVA_OPTS -cp $PWD/. org.springframework.boot.loader.JarLauncher
    
         state     since                  cpu      memory           disk             details
    #0   running   2018-01-23T11:41:45Z   333.4%   429.9M of 512M   178.9M of 512M
                            
Break
Data Ingestion/Query - TimeSeries Intro
  • The Time Series service enables you to quickly and efficiently ingest, store, and analyze time-series data.
  • The query API allows you to query data using groups, time ranges, values, aggregations, qualities, and filters

  • We are going to ingest several datasets and build our application around
Data Ingestion/Query - TimeSeries Architecture
  • Same Cloud service different use case:
    • Site A:
      Sensors produce time series data associated with a tag name and send it to Historian for storage and management. S2C subscribes to tags and collects generated data from those tags only. S2C requests a websocket connection from a gateway application that is used for data ingestion.
    • Site B:
      The WebSocket River establishes a connection when you first attempt a data transfer and keeps that channel open for as long as possible. Each data transfer verifies that the websocket connection is open. If the connection has been closed, the service opens a new connection. For more information about WebSocket River, see WebSocket River. To communicate with the Time Series gateway, the data must be structured as shown in Pushing Time Series Data.
    • Site C:
      Devices use an application to communicate directly with the websocket.
In our case we will focus only on case C direct websocket connection to Timeseries
Data Ingestion/Query - TimeSeries APIs
  • The Time series service offers two APIs:
    • WebSocket: for data ingestion
      wss://gateway-predix-data-services.run.asv-pr.ice.predix.io/v1/stream/messages
      • Supported Payload format: Json and GZIP Json
      • Decompressed Payload size cannot be greater than 512K
    • REST API: for data query and basic aggregation
      https://time-series-store-predix.run.asv-pr.ice.predix.io

The API endpoints are the same whatever TimeService service instance we created. To differentiate between instances there is a tenant ID named : predix-zone-id that needs to be included in any headers (https, wss) sent to Timeseries Services.
Data Ingestion/Query - TimeSeries Query API
POST /v1/datapoints
Payload:

{
  "tags": [
    {
      "name": "Compressor-2015:CompressionRatio",
      "order": "desc",
      "aggregations": [
        {
          "type": "avg",
          "sampling": {
            "unit": "s",
            "value": "30"
          }
        }
      ]
    }
  ],
  "start": 1452112200000,
  "end": 1453458896222
}
                        
GET /v1/tags
POST /v1/datapoints Payload:

{
  "start": "1y-ago",
  "tags": [
    {
      "name": "Compressor-2015:CompressionRatio",
      "order": "desc",
      "groups": [
        {
          "name": "quality"
        }
      ]
    }
  ]
}
                            
POST /v1/datapoints/latest Payload:

{
  "tags": [
    {
      "name": "Compressor-2015:CompressionRatio"
    }
  ]
}
                        
POST /v1/datapoints Payload:

{
  "cache_time": 0,
  "tags": [
    {
      "name": "Compressor-2015:CompressionRatio",
      "order": "desc",
      "limit": 2
    }
  ],
  "start": 1452112200000,
  "end": 1453458896222
}
                        
Cloud Foundry Interlude - Service Management - Devbox
  • List all services available:
    • cf marketplace
  • Create a service:
    • cf create-service {service name} {service plan} {service instance name}
  • Bind an application to a running service
    • cf bind-service {App Name} {service instance name}
Cloud Foundry Interlude - Service Management
  • Each time you bind a service to an application an environment variables called VCAP is updated
  • This variable contains a json object that contains various information:
    • Credentials
    • Zone id
    • Dashboard url
  • Various framework abstract the access to this variable to handle service authentication transparently and service discovery
  • A binded service cannot be deleted :

predix delete-service uaa-foundry

Really delete the service uaa-foundry?> y
Deleting service uaa-foundry in org European_Foundry_Basic / space demos-dev as Benoit.Laurent@ge.com...
FAILED
Cannot delete service instance, service keys and bindings must first be deleted
                            

{
  "staging_env_json": {},
  "running_env_json": {},
  "environment_json": {},
  "system_env_json": {
    "VCAP_SERVICES": {
      "predix-uaa": [
        {
          "credentials": {
            "issuerId": "https://demos-dev.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token",
            "subdomain": "demos-dev",
            "zone": {
              "http-header-value": "f082b5bf-37b6-4720-9a9b-fe06efd274d0",
              "http-header-name": "X-Identity-Zone-Id"
            },
            "uri": "https://demos-dev.predix-uaa.run.aws-usw02-pr.ice.predix.io"
          },
          "syslog_drain_url": null,
          "volume_mounts": [],
          "label": "predix-uaa",
          "provider": null,
          "plan": "Tiered",
          "name": "uaa-foundry",
          "tags": []
        }
      ]
    }
  },
  "application_env_json": {
    "VCAP_APPLICATION": {
      "limits": {
        "fds": 16384,
        "mem": 1024,
        "disk": 1024
      },
      "application_name": "cfapp-UBmZpR",
      "application_uris": [],
      "name": "cfapp-UBmZpR",
      "space_name": "demos-dev",
      "space_id": "f902086a-c66d-44f6-a1d6-1e92c4f0fef8",
      "uris": [],
      "users": null,
      "application_id": "c9596a69-b144-4950-b726-16afa8273133",
      "version": "d2579a7d-d8c0-4750-bc9f-a62059b22cdd",
      "application_version": "d2579a7d-d8c0-4750-bc9f-a62059b22cdd"
    }
  }
}
                    
Cloud Foundry Interlude - Service Management - Devbox
  • Each service exposed to public IP needs to be registered against at least one UAA service
    • Create a service with UAA authentication:
      • cf create-service {service name} {service plan} {service name instance} -c '{"trustedIssuerIds":["'${uaaIssuerId}'"]}'
      • uaaIssuerId can be retrieved using env variables of an app binded to a service
  • How to get the uaaIssuerId ?
    • cf bind-service {App Name} {service instance name}
    • cf env {App Name}
      • Search for a issuerId tag
Cloud Foundry Interlude - Env Automation - Devbox & OSX
Easy env access

export cftempapp_dir=`mktemp -d /tmp/cfapp-XXXXXX`
cd $cftempapp_dir
export cftempapp_name=${PWD##*/}
touch Procfile
cf push $cftempapp_name -b http://github.com/ryandotsmith/null-buildpack.git --no-start --no-route -m 32m >/dev/null
cf bs $cftempapp_name uaa-lab >/dev/null
export cftempapp_guid=`cf app uaa-lab --guid`
cf curl /v2/apps/$cftempapp_guid/env
cf delete $cftempapp_name -f >/dev/null
cd
rm -f $cftempapp_dir/Procfile
rmdir $cftempapp_dir
                    
As you can see there some inter dependencies between services, it's important to be able to script service configuration as much as possible
This task of fetching fields from the VCAP variable is done so often that a script is really handy:
This will output the VCAP containing information related to all those services.
Lab - TimesSeries Devbox
  • Create a client named timeseries-ingestion-client using predix cli
  • Create a predix-timeseries services using the predix cli named timeseries-lab
  • Automated way:
    
    predix create-service predix-timeseries Free timeseries-lab uaa-lab -c timeseries-ingestion-client -s timeseries-ingestion-client
                                    
Data Ingestion/Query - Data Ingestion
  • To perform data ingestion we need to open a web socket with a header containing :
    • Authorization: Bearer {TOKEN}
    • Predix-Zone-Id: {ZONE}
    • Origin: {Origin}
  • Once the web socket is opened we can send our data as a json payload
    
    {
       "messageId":"{MessageID}",
       "body":[
          {
             "name":"{TagName (Required)}",
             "datapoints":[
                [
                   "{EpochInMs (Required)}",
                   "{Measure (Required)}",
                   "{Quality (Optional)}"
                ]
             ],
             "attributes (Optional)":{
                "{AttributeKey}":"{AttributeValue}",
                "{AttributeKey2}":"{AttributeValue2}"
             }
          }
       ]
    }
                                
  • Accessing a Timeseries service require a token generated by UAA service using
    Resource access aka client_credentials
Predix Dev Tools Interlude - Predix Toolkit
  • It's often desirable to be able to execute some simple operation like
    • open web socket
    • test uaa client
    • Test Predix services API
    without having to create our own script each time.
  • For this reason the Predix Team created the predix-toolkit tool UI : Predix Toolkit
  • Depending on the data center you are using select the proper Availibility Zones (US West) for Us
Lab - TimesSeries Devbox
  • Using the predix toolkit tools API explorer try to open a web socket to the timeseries service
  • This can be done in the API explorer tab
  • Login as Client :
    • id: timeseries-ingestion-client
    • secret: timeseries-ingestion-client
    the client you just created and use the uaa url :
    https://lab-{your initials}.predix-uaa.run.aws-usw02-pr.ice.predix.io
  • Go to Time Series Ingest item and put your predix-zone-id you've retrieved previously
  • Click on Open Socket
Lab - Time Series Query Devbox
  • Open the Time Series Query item
  • Select Limited Datapoints
  • Change 1y into 3y
  • Enter the predix-zone-id
  • Press Submit

{
  "tags": [
    {
      "name": "Compressor-2015:CompressionRatio",
      "results": [
        {
          "groups": [
            {
              "name": "type",
              "type": "number"
            }
          ],
          "values": [
            [
              1453338377222,
              10,
              1
            ],
            [
              1453338377222,
              10,
              1
            ]
          ],
          "attributes": {
            "customer": [
              "Acme"
            ],
            "host": [
              "server2"
            ],
            "valid": [
              "true"
            ]
          }
        }
      ],
      "stats": {
        "rawCount": 2
      }
    }
  ]
}
                    
Lab - Time Series Open Data ingestion Devbox
  • Execute the following script download.sh this will download few datasets
  • Get the ingester client in the predix starter repository
  • Deploy it in your space with cf p

  • For your information this ingester is a python app that use plain web socket to perform the ingestion
    • Open ingest.py and check the openWSS
    • Get the ingester client in the predix starter repository
    • Deploy it in your space with cf p

    • For your information this ingester is a python app that use plain web socket to perform the ingestion
    • We find our headers parameters just as described previously
Lab - Time Series Open Data ingestion Devbox
  • Open Postman and create a new POST request to
    https://timeseries-ingestion-service-{your random route}.run.aws-usw02-pr.ice.predix.io/upload
  • Use the OAuth 2.0 helper to get a Bearer Token (use Client Credential in Grant Type)
  • In Body select form-data set key to files[] and value set to path to a json file (Use drop down to change the type to file)
  • Wait...

  • Using predix-toolkit check that you have new tags and new values with the Limited Datapoints Query
Data Ingestion/Query - Multi Language Client
TimeSeries Data Ingestion - Java Client
The ingestion is done using a web socket but you need to implement OAuth2, reconnection, it's simpler to use the SDK
  • Predix offers a Java SDK
  • The first things we need is to create a TenantContext
    
    try {
        tenantContext = TenantContextFactory.createTenantContextFromPropertiesFile("/predix-timeseries.properties");
    } catch (URISyntaxException | PredixTimeSeriesException | IOException e) {
        e.printStackTrace();
    }
                                
    There is several ways to create this tenant from a properties files, arguments, ... Have a look to this class for more information:
    
    com.ge.predix.timeseries.client.TenantContextFactory
                               
  • The rest of the code is just POJO filling
    
    IngestionRequestBuilder ingestionBuilder = IngestionRequestBuilder
                                    .createIngestionRequest()
                                    .withMessageId(timestamp)
                                    .addDataPoints(
                                        Arrays.asList(
                                            new DataPoint(new Date().getTime(), sensorValueAsInt, Quality.GOOD),
                                            new DataPoint(new Date().getTime(), sensorValueAsDouble, Quality.NOT_APPLICABLE),
                                            new DataPoint(new Date().getTime(), "Bad Value", Quality.BAD),
                                            new DataPoint(new Date().getTime(), null, Quality.UNCERTAIN)
                                        )
                                    )
                                    .addAttribute("AttributeKey", "AttributeValue")
                                    .addAttribute("AttributeKey2", "AttributeValue2")
                                    .build());
                                
Lab - TimeSeries Data Ingestion - Java Client
  • Once the message have been created we need to extract the json:
    
    String json = ingestionBuilder.build().get(0);
                                
  • We can now send the message over the wire:
    
    IngestionResponse response = ClientFactory.ingestionClientForTenant(ingestionTenant).ingest(json);
    String responseStr = response.getMessageId() + response.getStatusCode();
                                
  • ⚠ The error code is not obvious if you get a exception at this point like PredixTimeSeriesException Just simply retry the ingestionClientForTenant web socket reconnection is handled by the SDK
Lab - TimeSeries Data Ingestion - Java Application
  • Checkout the branch timeseries and go to the directory simple-predix-service
  • This application contains a simple java application exposing a REST endpoint /api/v1/sensor
  • For Performance concerns the ingestion is not done each request but in batch fashion
  • Everything is done in the ingester package
  • To Deploy this application go to directory simple-predix-service
    
    predix cf p
                                
    Once deployed you can ingest some points using postman
  • Create a POST request and put https://XXX.predix.io/api/v1/sensor and add as body
    
    {
        "value":{{$randomInt}},
        "deviceId":"osx90r43y2",
        "ip": "192.168.1.1",
        "type":"mac",
        "msec": {{$timestamp}}
    }
                                
    Do not forget the token...
  • Using predix toolkit check retreive your tags
  • Retreive some data points
TimeSeries Data Query - Java Application
  • Doing a query is very simple Just a REST call
    • Do it by hand using any REST capable toolkit or
    • Use the Java SDK

  • Two parts as for ingestion
    • Create Your Query Object
      
      QueryBuilder builder = QueryBuilder.createQuery()
              .withStartAbs(1427463525000L)
              .withEndAbs(1427483525000L)
              .addTags(
                      QueryTag.Builder.createQueryTag()
                              .withTagNames(Arrays.asList("ALT_SENSOR", "TEMP_SENSOR"))
                              .withLimit(1000)
                              .addAggregation(Aggregation.Builder.averageWithInterval(1, TimeUnit.HOURS))
                              .addFilters(FilterBuilder.getInstance()
                                      .addMeasurementFilter(FilterBuilder.Condition.GREATER_THAN_OR_EQUALS, Arrays.asList("23.1")).build())
                              .addFilters(FilterBuilder.getInstance()
                                      .withQualitiesFilter(Arrays.asList(Quality.BAD, Quality.GOOD)).build())
                              .build());
                                          
    • Then start the query:
      
      QueryResponse response = ClientFactory.queryClientForTenant(tenant).queryAll(builder.build());
                                          
    • The response can be return as string after converting it to json
Lab - TimeSeries Data Query - Java Application
  • Implement a REST endpoint in the SensorController
  • Three Request Params
    • tag tag that should be queried
    • startDate in millisec the starting date
    • endDate in millisec the ending date
  • Pay attention to the return value it has to be json
  • You can try to your query using postman :
    
    https://simple-predix-service-xxx-yyy.run.aws-usw02-pr.ice.predix.io/api/v1/sensor?tag=ZZZZ&startDate=OOOO&endDate={{$timestamp}}000
                                
  • One solution
    
    @GetMapping(path="/sensor", produces = "application/json")
    String read(@RequestParam("tag") String tag, @RequestParam("startDate") String startDate, @RequestParam("endDate") String endDate) throws IOException, PredixTimeSeriesException {
        QueryBuilder builder = null;
            builder = QueryBuilder.createQuery()
                    .withStartAbs(Long.parseLong(startDate))
                    .withEndAbs(Long.parseLong(endDate))
                    .addTags(
                            QueryTag.Builder.createQueryTag()
                                    .withTagNames(Arrays.asList(tag))
                                                   .build());
    
        QueryResponse response = null;
            response = ClientFactory.queryClientForTenant(ingester.getTenantContext()).queryAll(builder.build());
    
        Gson gson = new Gson();
        return gson.toJson(response);
    }
                                
Lab - TimeSeries Data Ingestion - Python SDK
  • You can do it by hand if you are familiar with python and websocket or
  • Use the predix package
  • it's very easy to setup
    • First update your manifest.yml file make sure you have everything configured :
      
      services:
        - uaa-xxx
        - timeseries-xxx
      env:
        PREDIX_APP_CLIENT_ID: rest-client
        PREDIX_APP_CLIENT_SECRET: rest-client
                                          
    • Then every where you want to ingest some data:
      
      import predix.data.timeseries
      ts = predix.data.timeseries.TimeSeries()
      ts.send('tag name', value)
                                          
  • try to add a post methods to the DHandler class to ingest this payload:
    
    {
      "value":{{$randomInt}},
      "deviceId":"osx90r43y2",
      "ip": "192.168.1.1",
      "type":"mac",
      "sec": {{$timestamp}},
      "usec": 0
    }
                                
Lab - TimeSeries Data Ingestion - Python Application
  • Here is one possible solution, by sending directly data to timeseries as soon as possible
    
    from flask import request
    from flask_restful import Resource
    import predix.data.timeseries
    import logging
    
    
    class DHandler(Resource):
    
        def __init__(self):
            self.ts = predix.data.timeseries.TimeSeries()
    
        def get(self):
            logging.debug(request.headers)
            return 'Hello World!', 200
    
        def post(self):
            logging.debug(request.headers)
            logging.debug(request.json)
            data = request.json
            self.ts.send(data['type']+'-'+data['deviceId'], data['value'])
            return 200
                                
  • This solution could be improve by taking advantage of the ts.queue method see here
Lab - TimeSeries Data Query - Python Application
  • For Query you can use the SDK to simplify your development
  • The SDK support get_datapoints see doc here to do the query for you
  • Update the get method of the handler to query your recently ingested data
  • You will use Postman to test using this request:
    
    https://simple-predix-service-xxx-yyy.run.aws-usw02-pr.ice.predix.io/api/v1/drones?tag=ZZZZ&startDate=1422022587000&endDate={{$timestamp}}000
                                
  • One solution
    
    def get(self):
        retval = self.ts.get_datapoints(tags=request.args['tag'], start=int(request.args['startDate']), end=int(request.args['endDate']))
        return retval, 200
                                
Break

Data Management Lvl 2 and Descriptive Analytics

  •   9:00 – 10:45 Blob & SQL
  • 10:45 – 11:00 Break
  • 11:00 – 12:45 Asset & Key-Value
  • 12:45 – 13:45 Lunch Break
  • 13:45 – 14:45 Predix UI : Tooling Review & Vis Introduction
  • 14:45 – 15:00 Break
  • 15:00 – 17:00 Data Visualization Lvl 1
  • 17:00 – 17:30 Q&A
Data Management Lvl 2 - Blob - Simple Storage Service
  • Highly scalable data storage in-the-cloud
  • Programmatic access via web services API
  • Web store =! Filesystem
    • Optimized for WORM (write once, read many)
    • Eventually consistent
  • Fast
    • But with a relatively high latency
  • Highly available and durable !
Data Management Lvl 2 - Blob - Eventually consistent
  • Updates to a key are atomic, succeed or fail. No partial update Replication across S3 can take some time
    • Write
      • Write then read, could report key does not exist
      • Write then list, might not include key in list
      • Overwrite then read, old data could be returned
    • Delete
      • Delete then read, could still get old data
      • Delete then list, deleted key could be included in list
Data Management Lvl 2 - Blob - Storage structure
  • Bucket == Root
  • Bucket name must be globaly unique
    • companyname-project-xxx
Data Management Lvl 2 - Blob - Storage structure
Lab - Blob in CF
  • Using the command line create a blob service named blob-lab
  • predix cs predix-blobstore Tiered blob-lab -x
    • -x make it exposed to public internet
  • You get some info after the creation:
    
    {
      "access_key_id": "AKIAJTOHTWLM3R5L5OEA",
      "bucket_name": "bucket-2f260863-d236-4ed2-9952-c42523804578",
      "host": "s3-us-west-2.amazonaws.com",
      "secret_access_key": "x4RSwDCKVQpWYdFZQgyebIgqlL80FaeEjgFMRdZ0",
      "url": "https://bucket-2f260863-d236-4ed2-9952-c42523804578.s3-us-west-2.amazonaws.com"
    }
                        
  • The CF Blob service is in fact powered by AWS S3 !!!
Blob - Multi Language
Lab - Using Blob Service with Python - Devbox
  • Make sur that you have cloned the repository :https://github.com/BLaurent/predix-starter
  • Go to the simple-predix-service-python directory
  • Checkout the branch blob, git checkout blob
    • If you have issue with modified files locally execute git stash
  • Check that manifest.yml file to make sure that you have everything setup
    
    ---
    applications:
      - name: simple-predix-service-python
        random-route: true
        timeout: 160
        memory: 256M
        disk_quota: 512M
        buildpack: https://github.com/cloudfoundry/python-buildpack.git
        services:
          - uaa-lab
          - blob-lab
        env:
          PREDIX_APP_CLIENT_ID: timeseries-ingestion-service
          PREDIX_APP_CLIENT_SECRET: timeseries-ingestion-service
                                
Lab - Blobstore with Python
  • Wait there is no token here ? Indeed blobstore rely on a different security mechanism
  • Predix py package will parse the environment of the application and figure out the credentials
  • Update the POST to allow a user to upload a file to blobstore using your api
    • You can find the documentation here
    • Flask can help you quite a lot in this process here
  • You may need to investigate json parsing of datetime object
Lab - Blobstore with Python
  • Update the GET to list the content of the bucket uploaded in the blobstore using your api
    • You can find the documentation here
  • Try to make the output pretty, avoid the double json encoding
Lab - Blobstore with Python

class DateTimeEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return o.isoformat()

class DHandler(Resource):

    def __init__(self):
        self.bs = predix.data.blobstore.BlobStore()

    def _allowed_file(self, filename):
        return '.' in filename and \
               filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

    def get(self):
        logging.debug(request.headers)
        return json.loads(json.dumps(self.bs.list_objects()['Contents'], cls=DateTimeEncoder)), 200

    def post(self):
        logging.debug(request.headers)
        logging.debug(request.json)
        for file in dict(request.files)['files[]']:
            if self._allowed_file(file.filename):
                filename = secure_filename(file.filename)
                path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
                file.save(path)
                return self.bs.upload_file(path, file.filename), 200
            else:
                return file.filename + ' not ingested. File type not allowed.'
                    
Data Management Lvl 2 - Asset - The other kind of database
  • Asset is meant to represent industrial assets
  • You have a notion of collection kind of template
    • The format to describe the asset is free
    • Only one constraints : uri you must have this field
  • You create links between asset using the uri
Data Management Lvl 2 - Asset - The other kind of database
  • Asset is meant to represent industrial assets
  • You have a notion of collection kind of template
    • The format to describe the asset is free
    • Only one constraints : uri you must have this field
  • You create links between asset using the uri
Data Management Lvl 2 - Asset - The other kind of database
Data Management Lvl 2 - Asset - The other kind of database
This is how you describe our manufacturers:

[
  { "uri": "/manufacturers/GE",
    "name": "General Electric Transportation",
    "year_founded": "1892" ,
    "hqLatLng":{"lat": 41.881138, "lng": -87.640666 }
  },

  {"uri": "/manufacturers/electro-motive-diesel",
    "name": "Electro-Motive Diesel",
    "year_founded": "1922",
    "hqLatLng": {"lat": 41.798091, "lng": -87.849247}
  },

  {"uri": "/manufacturers/national-railway-equipment",
    "name": "National Railway Equipment",
    "year_founded": "1984",
    "hqLatLng": {"lat": 41.515181 , "lng":-90.411707 }
  },

  {"uri": "/manufacturers/cummins",
    "name": "Cummins",
    "year_founded": "1919",
    "hqLatLng": {"lat":39.204000, "lng": -85.922910}
  }
]
                    
Data Management Lvl 2 - Asset - The other kind of database
This is how you describe our engines:

[
  {
    "uri": "/engines/v12-1",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-2",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-3",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-4",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-5",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-6",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-7",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-8",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-9",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-10",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-11",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-12",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-13",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-14",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-15",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-16",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-17",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-18",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v12-19",
    "type": "7FDL",
    "horsepower": "4400",
    "stroke": "230",
    "bore": "220",
    "RPM": "2400",
    "manufacturer": "/manufacturers/GE"
  },

  {
    "uri": "/engines/v16-1",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-2",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-3",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-4",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-5",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-6",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-7",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-8",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-9",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-10",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-11",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-12",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-13",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-14",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-15",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-16",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-17",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-18",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },
  {
    "uri": "/engines/v16-19",
    "type": "7FD",
    "horsepower": "4400",
    "stroke": "210",
    "bore": "220",
    "RPM": "900",
    "manufacturer": "/manufacturers/GE"
  },

  {
    "uri": "/engines/v16-2-1",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-2",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-3",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-4",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-5",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-6",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-7",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-8",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-9",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-10",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-11",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-12",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },
  {
    "uri": "/engines/v16-2-13",
    "type": "710",
    "horsepower": "4300",
    "stroke": "250",
    "bore": "200",
    "RPM": "215",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },

  {
    "uri": "/engines/645",
    "type": "7FDL",
    "horsepower": "1500",
    "stroke": "190",
    "bore": "230",
    "RPM": "900",
    "manufacturer": "/manufacturers/electro-motive-diesel"
  },

  {
    "uri": "/engines/QSK19-1",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-2",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-3",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-4",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-5",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-6",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-7",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-8",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-9",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-10",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-11",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-12",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-13",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-14",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-15",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-16",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-17",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },{
  "uri": "/engines/QSK19-18",
  "type": "Diesel",
  "horsepower": "2100",
  "stroke": "159",
  "bore": "159",
  "RPM": "800",
  "manufacturer": "/manufacturers/cummins"
},{
  "uri": "/engines/QSK19-19",
  "type": "Diesel",
  "horsepower": "2100",
  "stroke": "159",
  "bore": "159",
  "RPM": "800",
  "manufacturer": "/manufacturers/cummins"
},
  {
    "uri": "/engines/QSK19-20",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-21",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-22",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-23",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-24",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-25",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-26",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-27",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },
  {
    "uri": "/engines/QSK19-28",
    "type": "Diesel",
    "horsepower": "2100",
    "stroke": "159",
    "bore": "159",
    "RPM": "800",
    "manufacturer": "/manufacturers/cummins"
  },


  {
    "uri": "/engines/v20-1",
    "type": "7FDL",
    "horsepower": "5000",
    "stroke": "230",
    "bore": "230",
    "RPM": "650",
    "manufacturer": "/manufacturers/national-railway-equipment"
  },
  {
    "uri": "/engines/v20-2",
    "type": "7FDL",
    "horsepower": "5000",
    "stroke": "230",
    "bore": "230",
    "RPM": "650",
    "manufacturer": "/manufacturers/national-railway-equipment"
  },
  {
    "uri": "/engines/v20-3",
    "type": "7FDL",
    "horsepower": "5000",
    "stroke": "230",
    "bore": "230",
    "RPM": "650",
    "manufacturer": "/manufacturers/national-railway-equipment"
  },
  {
    "uri": "/engines/v20-4",
    "type": "7FDL",
    "horsepower": "5000",
    "stroke": "230",
    "bore": "230",
    "RPM": "650",
    "manufacturer": "/manufacturers/national-railway-equipment"
  },
  {
    "uri": "/engines/v20-5",
    "type": "7FDL",
    "horsepower": "5000",
    "stroke": "230",
    "bore": "230",
    "RPM": "650",
    "manufacturer": "/manufacturers/national-railway-equipment"
  },
  {
    "uri": "/engines/v20-6",
    "type": "7FDL",
    "horsepower": "5000",
    "stroke": "230",
    "bore": "230",
    "RPM": "650",
    "manufacturer": "/manufacturers/national-railway-equipment"
  },
  {
    "uri": "/engines/v20-7",
    "type": "7FDL",
    "horsepower": "5000",
    "stroke": "230",
    "bore": "230",
    "RPM": "650",
    "manufacturer": "/manufacturers/national-railway-equipment"
  }
]
                    
Data Management Lvl 2 - Asset - The other kind of database
This is how you describe our fleet:

[
  {
    "uri": "/fleets/up-1",
    "name": "Union Pacific Fleet 1",
    "customer": "/customers/union-pacific"
  },
  {
    "uri": "/fleets/up-2",
    "name": "Union Pacific Fleet 2",
    "customer": "/customers/union-pacific"
  },
  {
    "uri": "/fleets/up-3",
    "name": "Union Pacific Fleet 3",
    "customer": "/customers/union-pacific"
  },
  {
    "uri": "/fleets/up-4",
    "name": "Union Pacific Fleet 4",
    "customer": "/customers/union-pacific"
  }
, {
  "uri": "/fleets/up-5",
  "name": "Union Pacific Fleet 5",
  "customer": "/customers/union-pacific"
  },

  {
  "uri": "/fleets/bnsf-1",
  "name": "Burlington Northern Fleet 1",
  "customer": "/customers/burlington-northern-santa-fe"
  },
  {
    "uri": "/fleets/bnsf-2",
    "name": "Burlington Northern Fleet 2",
    "customer": "/customers/burlington-northern-santa-fe"
  },
  {
  "uri": "/fleets/bnsf-3",
  "name": "Burlington Northern Fleet 3",
  "customer": "/customers/burlington-northern-santa-fe"
  },
  {
    "uri": "/fleets/bnsf-4",
    "name": "Burlington Northern Fleet 4",
    "customer": "/customers/burlington-northern-santa-fe"
  },
  {
  "uri": "/fleets/bnsf-5",
  "name": "Burlington Northern Fleet 5",
  "customer": "/customers/burlington-northern-santa-fe"
  },
  {
    "uri": "/fleets/csx-1",
    "name": "CSX Fleet 1",
    "customer": "/customers/csx"
  },
  {
    "uri": "/fleets/csx-2",
    "name": "CSX Fleet 2",
    "customer": "/customers/csx"
  },
  {
    "uri": "/fleets/csx-3",
    "name": "CSX Fleet 3",
    "customer": "/customers/csx"
  },
  {
    "uri": "/fleets/ns-1",
    "name": "Norfolk Southern Fleet 1",
    "customer": "/customers/norfolk-southern"
  },
  {
    "uri": "/fleets/ns-2",
    "name": "Norfolk Southern Fleet 2",
    "customer": "/customers/norfolk-southern"
  },

  {
    "uri": "/fleets/cn-1",
    "name": "Canadian National Fleet 1",
    "customer": "/customers/canadian-national"
  },
  {
    "uri": "/fleets/cn-2",
    "name": "Canadian National Fleet 2",
    "customer": "/customers/canadian-national"
  },
  {
    "uri": "/fleets/cn-3",
    "name": "Canadian National Fleet 3",
    "customer": "/customers/canadian-national"
  },
  {
    "uri": "/fleets/cn-4",
    "name": "Canadian National Fleet 4",
    "customer": "/customers/canadian-national"
  },
  {
    "uri": "/fleets/cn-5",
    "name": "Canadian National Fleet 5",
    "customer": "/customers/canadian-national"
  },
  {
    "uri": "/fleets/cn-6",
    "name": "Canadian National Fleet 6",
    "customer": "/customers/canadian-national"
  },
  {
    "uri": "/fleets/cn-7",
    "name": "Canadian National Fleet 7",
    "customer": "/customers/canadian-national"
  },
  {
    "uri": "/fleets/cn-8",
    "name": "Canadian National Fleet 8",
    "customer": "/customers/canadian-national"
  }
]
                    
Data Management Lvl 2 - Asset - The other kind of database
This is how you describe our locomotives:

[
    {
        "uri": "/locomotives/1",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "001",
        "emission_tier": "0+",
        "fleet": "/fleets/up-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-1",
        "installedOn": "01/12/2005",
        "dateIso": "2005-12-01T13:15:31Z",
        "hqLatLng": {
            "lat": 33.914605,
            "lng": -117.253374
        }
    },
    {
        "uri": "/locomotives/2",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "002",
        "emission_tier": "0+",
        "fleet": "/fleets/up-1",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-1",
        "hqLatLng": {
            "lat": 47.655492,
            "lng": -117.427025
        }
    },
    {
        "uri": "/locomotives/3",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "003",
        "emission_tier": "0+",
        "fleet": "/fleets/up-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-2",
        "installedOn" : "02/12/2005",
        "dateIso": "2005-12-02T13:15:31Z",
        "hqLatLng": {
            "lat": 46.860395,
            "lng": -109.473494
        }
    },
    {
        "uri": "/locomotives/4",
        "type": "Diesel-electric",
        "model": "AD40SPe",
        "serial_no": "004",
        "emission_tier": "0+",
        "fleet": "/fleets/up-2",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-2",
        "hqLatLng": {
            "lat": 45.784314,
            "lng": -108.500856
        }
    },
    {
        "uri": "/locomotives/5",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "005",
        "emission_tier": "0+",
        "fleet": "/fleets/up-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-3",
        "installedOn": "10/12/2005",
        "dateIso": "2005-12-10T13:15:31Z",
        "hqLatLng": {
            "lat": 35.022757,
            "lng": -83.009365
        }
    },
    {
        "uri": "/locomotives/6",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "006",
        "emission_tier": "0+",
        "fleet": "/fleets/up-2",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-3",
        "installedOn": "11/12/2005",
        "dateIso": "2005-12-11T13:15:31Z",
        "hqLatLng": {
            "lat": 33.120118,
            "lng": -81.563755
        }
    },
    {
        "uri": "/locomotives/7",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "007",
        "emission_tier": "0+",
        "fleet": "/fleets/up-3",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-4",
        "installedOn": "12/12/2005",
        "dateIso": "2005-12-12T13:15:31Z",
        "hqLatLng": {
            "lat": 36.739438,
            "lng": -79.66268
        }
    },
    {
        "uri": "/locomotives/8",
        "type": "Diesel-electric",
        "model": "GL23TCe",
        "serial_no": "008",
        "emission_tier": "0+",
        "fleet": "/fleets/up-3",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-4",
        "hqLatLng": {
            "lat": 41.876813,
            "lng": -113.717141
        }
    },
    {
        "uri": "/locomotives/9",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "009",
        "emission_tier": "0+",
        "fleet": "/fleets/up-3",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-5",
        "hqLatLng": {
            "lat": 36.669408,
            "lng": -115.617391
        }
    },
    {
        "uri": "/locomotives/10",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "0010",
        "emission_tier": "0+",
        "fleet": "/fleets/up-4",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-5",
        "hqLatLng": {
            "lat": 47.941049,
            "lng": -100.126484
        }
    },
    {
        "uri": "/locomotives/11",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "0011",
        "emission_tier": "0+",
        "fleet": "/fleets/up-4",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-6",
        "hqLatLng": {
            "lat": 32.7086,
            "lng": -108.190375
        }
    },
    {
        "uri": "/locomotives/12",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "0012",
        "emission_tier": "0+",
        "fleet": "/fleets/up-4",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-6",
        "hqLatLng": {
            "lat": 34.099394,
            "lng": -90.261741
        }
    },
    {
        "uri": "/locomotives/13",
        "type": "Diesel-electric",
        "model": "GL23TCe",
        "serial_no": "0013",
        "emission_tier": "0+",
        "fleet": "/fleets/up-5",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-7",
        "hqLatLng": {
            "lat": 36.050366,
            "lng": -91.340363
        }
    },
    {
        "uri": "/locomotives/14",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "0014",
        "emission_tier": "0+",
        "fleet": "/fleets/up-5",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-7",
        "hqLatLng": {
            "lat": 35.931077,
            "lng": -79.643374
        }
    },
    {
        "uri": "/locomotives/15",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "0015",
        "emission_tier": "0+",
        "fleet": "/fleets/up-5",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-8",
        "hqLatLng": {
            "lat": 45.314087,
            "lng": -90.969283
        }
    },
    {
        "uri": "/locomotives/16",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0016",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-1",
        "hqLatLng": {
            "lat": 47.781463,
            "lng": -116.940009
        }
    },
    {
        "uri": "/locomotives/17",
        "type": "",
        "model": "B40-8",
        "serial_no": "0017",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-8",
        "hqLatLng": {
            "lat": 32.863603,
            "lng": -97.327141
        }
    },
    {
        "uri": "/locomotives/18",
        "type": "Diesel-electric",
        "model": "MP15AC",
        "serial_no": "0018",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-1",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/645",
        "hqLatLng": {
            "lat": 42.090136,
            "lng": -102.875767
        }
    },
    {
        "uri": "/locomotives/19",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0019",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-1",
        "hqLatLng": {
            "lat": 32.147267,
            "lng": -83.87744
        }
    },
    {
        "uri": "/locomotives/20",
        "type": "Diesel-electric",
        "model": "ES44DC",
        "serial_no": "0020",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-9",
        "hqLatLng": {
            "lat": 38.191536,
            "lng": -85.472672
        }
    },
    {
        "uri": "/locomotives/21",
        "type": "Diesel-electric",
        "model": "SD80MAC",
        "serial_no": "0021",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/v20-1",
        "hqLatLng": {
            "lat": 43.49188,
            "lng": -122.944105
        }
    },
    {
        "uri": "/locomotives/22",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0022",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-2",
        "hqLatLng": {
            "lat": 45.96556,
            "lng": -116.989515
        }
    },
    {
        "uri": "/locomotives/23",
        "type": "Diesel-electric",
        "model": "ES44DC",
        "serial_no": "0023",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-10",
        "hqLatLng": {
            "lat": 41.788715,
            "lng": -114.601854
        }
    },
    {
        "uri": "/locomotives/24",
        "type": "Diesel-electric",
        "model": "SD80MAC",
        "serial_no": "0024",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/v20-2",
        "hqLatLng": {
            "lat": 38.097843,
            "lng": -118.161825
        }
    },
    {
        "uri": "/locomotives/25",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0025",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-3",
        "hqLatLng": {
            "lat": 38.12204,
            "lng": -106.260561
        }
    },
    {
        "uri": "/locomotives/26",
        "type": "Diesel-electric",
        "model": "ES44DC",
        "serial_no": "0026",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-11",
        "hqLatLng": {
            "lat": 36.628722,
            "lng": -103.908348
        }
    },
    {
        "uri": "/locomotives/27",
        "type": "Diesel-electric",
        "model": "SD80MAC",
        "serial_no": "0027",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/v20-3",
        "hqLatLng": {
            "lat": 33.544224,
            "lng": -104.506977
        }
    },
    {
        "uri": "/locomotives/28",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0028",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-4",
        "hqLatLng": {
            "lat": 36.87125,
            "lng": -102.557858
        }
    },
    {
        "uri": "/locomotives/29",
        "type": "Diesel-electric",
        "model": "ES44DC",
        "serial_no": "0029",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-3",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-12",
        "hqLatLng": {
            "lat": 34.708707,
            "lng": -97.179306
        }
    },
    {
        "uri": "/locomotives/30",
        "type": "Diesel-electric",
        "model": "SD80MAC",
        "serial_no": "0030",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/v20-4",
        "hqLatLng": {
            "lat": 41.103915,
            "lng": -78.286601
        }
    },
    {
        "uri": "/locomotives/31",
        "type": "Diesel-electric",
        "model": "SD80MAC",
        "serial_no": "0031",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/v20-5",
        "hqLatLng": {
            "lat": 31.351436,
            "lng": -105.258803
        }
    },
    {
        "uri": "/locomotives/32",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0032",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-5",
        "hqLatLng": {
            "lat": 30.062855,
            "lng": -102.111381
        }
    },
    {
        "uri": "/locomotives/33",
        "type": "Diesel-electric",
        "model": "ES44DC",
        "serial_no": "0033",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-4",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-13",
        "hqLatLng": {
            "lat": 26.634179,
            "lng": -98.514328
        }
    },
    {
        "uri": "/locomotives/34",
        "type": "Diesel-electric",
        "model": "SD80MAC",
        "serial_no": "0034",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/v20-6",
        "hqLatLng": {
            "lat": 29.93051,
            "lng": -95.969945
        }
    },
    {
        "uri": "/locomotives/35",
        "type": "Diesel-electric",
        "model": "ES44DC",
        "serial_no": "0035",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-5",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-14",
        "hqLatLng": {
            "lat": 44.940593,
            "lng": -110.825888
        }
    },
    {
        "uri": "/locomotives/36",
        "type": "Diesel-electric",
        "model": "SD80MAC",
        "serial_no": "0036",
        "emission_tier": "0+",
        "fleet": "/fleets/bnsf-5",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/v20-7",
        "hqLatLng": {
            "lat": 41.307371,
            "lng": -104.574386
        }
    },
    {
        "uri": "/locomotives/37",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "0037",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-15",
        "hqLatLng": {
            "lat": 43.488604,
            "lng": -116.913932
        }
    },
    {
        "uri": "/locomotives/38",
        "type": "Diesel-electric",
        "model": "GL23TCe",
        "serial_no": "0038",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-1",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-9",
        "hqLatLng": {
            "lat": 40.26462,
            "lng": -74.496063
        }
    },
    {
        "uri": "/locomotives/39",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "0039",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-16",
        "hqLatLng": {
            "lat": 43.650927,
            "lng": -74.570743
        }
    },
    {
        "uri": "/locomotives/40",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "0040",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-2",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-10",
        "hqLatLng": {
            "lat": 41.394707,
            "lng": -83.905661
        }
    },
    {
        "uri": "/locomotives/41",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "0041",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-17",
        "hqLatLng": {
            "lat": 45.40759,
            "lng": -103.471649
        }
    },
    {
        "uri": "/locomotives/42",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "0042",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-2",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-11",
        "hqLatLng": {
            "lat": 42.284877,
            "lng": -99.513644
        }
    },
    {
        "uri": "/locomotives/43",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "0043",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-18",
        "hqLatLng": {
            "lat": 39.173252,
            "lng": -90.552123
        }
    },
    {
        "uri": "/locomotives/44",
        "type": "Diesel-electric",
        "model": "SD70ACe",
        "serial_no": "0044",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-2",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-12",
        "hqLatLng": {
            "lat": 40.775543,
            "lng": -123.336355
        }
    },
    {
        "uri": "/locomotives/45",
        "type": "Diesel-electric",
        "model": "ES44AC",
        "serial_no": "0045",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-3",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v12-19",
        "hqLatLng": {
            "lat": 41.394707,
            "lng": -119.079633
        }
    },
    {
        "uri": "/locomotives/46",
        "type": "Diesel-electric",
        "model": "GL23TCe",
        "serial_no": "0046",
        "emission_tier": "0+",
        "fleet": "/fleets/csx-3",
        "manufacturer": "/manufacturers/electro-motive-diesel",
        "engine": "/engines/v16-2-13",
        "hqLatLng": {
            "lat": 39.921845,
            "lng": -111.985095
        }
    },
    {
        "uri": "/locomotives/47",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0047",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-2",
        "hqLatLng": {
            "lat": 41.674215,
            "lng": -110.118111
        }
    },
    {
        "uri": "/locomotives/48",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0048",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-3",
        "hqLatLng": {
            "lat": 43.829065,
            "lng": -108.038521
        }
    },
    {
        "uri": "/locomotives/49",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0049",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-4",
        "hqLatLng": {
            "lat": 48.730575,
            "lng": -121.358846
        }
    },
    {
        "uri": "/locomotives/50",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0050",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-5",
        "hqLatLng": {
            "lat": 47.988616,
            "lng": -117.610012
        }
    },
    {
        "uri": "/locomotives/51",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0051",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-6",
        "hqLatLng": {
            "lat": 46.141609,
            "lng": -120.321935
        }
    },
    {
        "uri": "/locomotives/52",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0052",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-1",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-7",
        "hqLatLng": {
            "lat": 44.629206,
            "lng": -120.960034
        }
    },
    {
        "uri": "/locomotives/53",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0053",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-8",
        "hqLatLng": {
            "lat": 48.572493,
            "lng": -114.57904
        }
    },
    {
        "uri": "/locomotives/54",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0054",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-9",
        "hqLatLng": {
            "lat": 48.677936,
            "lng": -109.873057
        }
    },
    {
        "uri": "/locomotives/55",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0055",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-10",
        "hqLatLng": {
            "lat": 48.572493,
            "lng": -104.927787
        }
    },
    {
        "uri": "/locomotives/56",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0056",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-11",
        "hqLatLng": {
            "lat": 46.746176,
            "lng": -104.608737
        }
    },
    {
        "uri": "/locomotives/57",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0057",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-12",
        "hqLatLng": {
            "lat": 41.721683,
            "lng": -109.952819
        }
    },
    {
        "uri": "/locomotives/58",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0058",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-13",
        "hqLatLng": {
            "lat": 43.771496,
            "lng": -107.240897
        }
    },
    {
        "uri": "/locomotives/59",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0059",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-14",
        "hqLatLng": {
            "lat": 43.424917,
            "lng": -93.681285
        }
    },
    {
        "uri": "/locomotives/60",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0060",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-15",
        "hqLatLng": {
            "lat": 42.018661,
            "lng": -95.994395
        }
    },
    {
        "uri": "/locomotives/61",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0061",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-16",
        "hqLatLng": {
            "lat": 33.191119,
            "lng": -100.700378
        }
    },
    {
        "uri": "/locomotives/62",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0062",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-17",
        "hqLatLng": {
            "lat": 39.481317,
            "lng": -80.440722
        }
    },
    {
        "uri": "/locomotives/63",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0063",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-18",
        "hqLatLng": {
            "lat": 42.211153,
            "lng": -71.617224
        }
    },
    {
        "uri": "/locomotives/64",
        "type": "Diesel-electric",
        "model": "C40-8W ",
        "serial_no": "0064",
        "emission_tier": "0+",
        "fleet": "/fleets/ns-2",
        "manufacturer": "/manufacturers/GE",
        "engine": "/engines/v16-19",
        "hqLatLng": {
            "lat": 44.171027,
            "lng": -69.992906
        }
    },
    {
        "uri": "/locomotives/65",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0065",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-1",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-6",
        "hqLatLng": {
            "lat": 50.750961,
            "lng": -111.035917
        }
    },
    {
        "uri": "/locomotives/66",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0066",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-1",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-7",
        "hqLatLng": {
            "lat": 54.165178,
            "lng": -125.631473
        }
    },
    {
        "uri": "/locomotives/67",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0067",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-1",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-8",
        "hqLatLng": {
            "lat": 54.517653,
            "lng": -111.462679
        }
    },
    {
        "uri": "/locomotives/68",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0068",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-1",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-9",
        "hqLatLng": {
            "lat": 57.466212,
            "lng": -113.51957
        }
    },
    {
        "uri": "/locomotives/69",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0069",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-10",
        "hqLatLng": {
            "lat": 58.648156,
            "lng": -105.758545
        }
    },
    {
        "uri": "/locomotives/70",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0070",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-11",
        "hqLatLng": {
            "lat": 52.479733,
            "lng": -107.757597
        }
    },
    {
        "uri": "/locomotives/71",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0071",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-12",
        "hqLatLng": {
            "lat": 50.259228,
            "lng": -103.14772
        }
    },
    {
        "uri": "/locomotives/72",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0072",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-13",
        "hqLatLng": {
            "lat": 49.939207,
            "lng": -108.136696
        }
    },
    {
        "uri": "/locomotives/73",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0073",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-14",
        "hqLatLng": {
            "lat": 55.252082,
            "lng": -99.996572
        }
    },
    {
        "uri": "/locomotives/74",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0074",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-2",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-15",
        "hqLatLng": {
            "lat": 52.213168,
            "lng": -95.47359
        }
    },
    {
        "uri": "/locomotives/75",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0075",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-16",
        "hqLatLng": {
            "lat": 44.264612,
            "lng": -78.588661
        }
    },
    {
        "uri": "/locomotives/76",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0076",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-17",
        "hqLatLng": {
            "lat": 44.088886,
            "lng": -65.04682
        }
    },
    {
        "uri": "/locomotives/77",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0077",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-18",
        "hqLatLng": {
            "lat": 46.513021,
            "lng": -67.070163
        }
    },
    {
        "uri": "/locomotives/78",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0078",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-19",
        "hqLatLng": {
            "lat": 48.387737,
            "lng": -72.315866
        }
    },
    {
        "uri": "/locomotives/79",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0079",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-3",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-20",
        "hqLatLng": {
            "lat": 43.12077,
            "lng": -81.576942
        }
    },
    {
        "uri": "/locomotives/80",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0080",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-21",
        "hqLatLng": {
            "lat": 49.281775,
            "lng": -117.735624
        }
    },
    {
        "uri": "/locomotives/81",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0081",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-22",
        "hqLatLng": {
            "lat": 49.267239,
            "lng": -119.584813
        }
    },
    {
        "uri": "/locomotives/82",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0082",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-23",
        "hqLatLng": {
            "lat": 49.209053,
            "lng": -120.320033
        }
    },
    {
        "uri": "/locomotives/83",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0083",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-25",
        "hqLatLng": {
            "lat": 49.179934,
            "lng": -120.854738
        }
    },
    {
        "uri": "/locomotives/84",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0084",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-26",
        "hqLatLng": {
            "lat": 49.296307,
            "lng": -118.827314
        }
    },
    {
        "uri": "/locomotives/85",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0085",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-27",
        "hqLatLng": {
            "lat": 49.640363,
            "lng": -126.242998
        }
    },
    {
        "uri": "/locomotives/86",
        "type": "Diesel-electric",
        "model": "3GS21B",
        "serial_no": "0086",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-28",
        "hqLatLng": {
            "lat": 46.244201,
            "lng": -73.666786
        }
    },
    {
        "uri": "/locomotives/87",
        "type": "Diesel-electric",
        "model": "3GS21B-87",
        "serial_no": "0087",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-26",
        "hqLatLng": {
            "lat": 49.296307,
            "lng": -118.827314
        },
        "parent": "/locomotives/86",
        "installedOn": "05/12/2014",
        "dateIso": "2014-12-05T13:15:31Z"

    },
    {
        "uri": "/locomotives/88",
        "type": "Diesel-electric",
        "model": "3GS21B-88",
        "serial_no": "0085",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-27",
        "hqLatLng": {
            "lat": 49.640363,
            "lng": -126.242998
        },
        "parent": "/locomotives/87",
        "dateIso": "2015-11-05T13:15:30Z"

    },
    {
        "uri": "/locomotives/89",
        "type": "Diesel-electric",
        "model": "3GS21B-89",
        "serial_no": "0086",
        "emission_tier": "0+",
        "fleet": "/fleets/cn-4",
        "manufacturer": "/manufacturers/national-railway-equipment",
        "engine": "/engines/QSK19-28",
        "hqLatLng": {
            "lat": 46.244201,
            "lng": -73.666786
        },
        "parent": "/locomotives/88",
        "installedOn": "05/12/2015",
       "dateIso": "2015-12-05T13:15:31Z"
    }
]
                
Lab- Asset - The other kind of database
  • Create a client asset-client using the command line
  • Create an asset service asset-lab using the command line
  • Automated way:
    
    predix create-service predix-asset Free asset-lab uaa-lab -c asset-client -s asset-client
                                    
  • The result should be:
    
    Checking if client asset-client exists on service instance uaa-lab
    Client asset-client exists. The required authorities will be added to it.
    
    Creating service instance asset-lab in org European_Foundry_Basic / space machine as Benoit.Laurent@ge.com
    data: {"name":"asset-lab","space_guid":"40381acd-016a-4141-b5ee-ddf7bb6370d9","service_plan_guid":"f13446a3-c6a8-47cd-8bb3-f8c829c958ef","parameters":{"trustedIssuerIds":["https://uaa-bl.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token"]}}
    OK
    
    
    Updating client asset-client on Predix UAA instance uaa-lab
    OK
    
    Getting info for service instance asset-lab
    x§§§
    {
      "instanceId": "8fd77b47-8efe-474d-9533-b5d0273b50bc",
      "scriptEngine_uri": "https://8fd77b47-8efe-474d-9533-b5d0273b50bc.predix-script-engine.run.aws-usw02-pr.ice.predix.io",
      "uri": "https://predix-asset.run.aws-usw02-pr.ice.predix.io",
      "zone": {
        "http-header-name": "Predix-Zone-Id",
        "http-header-value": "8fd77b47-8efe-474d-9533-b5d0273b50bc",
        "oauth-scope": "predix-asset.zones.8fd77b47-8efe-474d-9533-b5d0273b50bc.user"
      }
    }
    Note: Depending on the service broker implementation this info may change
    Use the 'service-info' command to lookup the binding info for an app and service instance
                                
Lab- Asset - The other kind of database
  • Clean up you branch: git checkout -- .
  • Update your repository git pull
  • Update your branch git checkout asset
  • Go to the asset-setup directory
  • Update your python packages
  • pipenv update
  • make sure you are using the proper python version execute pipenv shell
  • 
    export VCAP_SERVICES=$(cat vcap_services.txt| jq --argjson asset_cred $(p si asset-lab | tail -n 12 | head -n 10 | jq -rc '.') -c '."predix-asset"[0].credentials=$asset_cred' | jq --argjson uaa_cred $(p si uaa-lab | tail -n 12 | head -n 10 | jq -rc '.') -c '."predix-uaa"[0].credentials=$uaa_cred')
    export PREDIX_APP_CLIENT_ID=asset-client
    export PREDIX_APP_CLIENT_SECRET=asset-client
                            
  • execute python app.py
Lab- Asset - The other kind of database
  • Open Postman create a request GET with https://predix-asset.run.aws-usw02-pr.ice.predix.io/{{collection}}
  • Where collection is the name of the collection e.g engines, locomotives,...
  • Update your credentials to use the asset-client and get a token
  • Fetch the collection
Lab- Asset - The other kind of database
  • open a python shell and type
    
    import predix.app
    import pprint
    manifest = predix.app.Manifest()
    asset = manifest.get_asset()
    print(asset.get_collections())
    pp = pprint.PrettyPrinter(indent=4)
                                
  • Asset Service support a mini query language: select all engines with RPM=2400
    
    pp.pprint(asset.get_collection(collection='/engines', filter='RPM=2400'))
                                
  • Select all engines with RPM=2400 but display only horsepower field
    
    pp.pprint(asset.get_collection(collection='/engines', filter='RPM=2400', fields='horsepower'))
                                
  • Select all locomotives of model SD70ACe and fleet up-5
    
    pp.pprint(asset.get_collection(collection='/locomotives', filter='model=SD70ACe:fleet=/fleets/up-5'))
                                
  • Select all locomotives with an engine v16-2-5 or from fleet csx-1
    
    pp.pprint(asset.get_collection(collection='/locomotives', filter='engine=/engines/v16-2-5|fleet=/fleets/csx-1'))
                                
Lab- Asset - The other kind of database
  • It's possible to enable audit log
  • To get the status : https://predix-asset.run.aws-usw02-pr.ice.predix.io/system/configs
  • With postman send a GET request to it,
    
    {
        "enableAudit": false
    }                           
  • Send a POST to this same url with a body:
    
    [
    {
    	"enableAudit" : true
    }
    ]
                                
  • Do a GET to check that the audit has been enabled
Lab- Asset - The other kind of database
  • You can update an asset by doing a PUT
  • On the shell do
    
    ass2=asset.get_collection(collection='/locomotives', filter='engine=/engines/v16-2-5')
    pp.pprint(ass2)
                                
    To fetch one locomotive asset
  • We will change the hqLatLng
    
    asset.patch_collection('locomotives/10', [{'op': 'replace', 'path': '/hqLatLng/lat', 'value': '40.941049'}])
    ass2 = asset.get_collection(collection='/locomotives', filter='engine=/engines/v16-2-5')
    pp.pprint(ass2)
                                
  • We can have display the list of change
    
    pp.pprint(asset.get_audit())
    pp.pprint(asset.get_audit_changes())
                                
Prescriptive Analytics Lvl1- Overview Px Analytics
  • Simplify developing advanced business analyses and deploying to production
  • Composed of three main components
    • Catalog
      • software catalog for sharing reusable analytics across development teams
      • facilitates deploying analytics into production
    • Runtime
      • cloud-based framework on which developers can implement, test, and deploy new combinations (orchestrations) of analytics
      • efficient, scalable, cloud-based approach for both the development and production
    • Ui
      • web application for data scientists to upload and test analytics
Prescriptive Analytics Lvl1- Public Px Analytics
  • Analytic Developers
    • Ability to deploy analytics as Cloud Foundry compatible microservices without code changes.
    • Ability to test and validate analytics in the cloud.
    • Faster development by facilitating discovery, publication, and analytic sharing through classification and cataloging.
  • Application Developers
    • Faster analytic solution development by abstracting the complexity of developing and deploying orchestrations in the cloud.
    • Ability to test and validate orchestrations in the cloud.
    • Faster analytic solution deployment by favoring configuration over coding. Changes to orchestration configurations (data sources, asset applicability, and so on) have been simplified
  • Supports analytics written in Java, Matlab and Python
Prescriptive Analytics Lvl1- Workflow Px Analytics
  1. Develop the Analytic Develop the analytic in either Java, Matlab, or Python. This work is done outside of Analytics Services
  2. Publish the Analytic to the Analytics Catalog. Add the analytic to the Analytics Catalog and upload the artifact
  3. Validate the Analytic
    • Test the analytic with a sample data set and verify the results using the Analytics Catalog service
    • If necessary, modify the analytic and upload new artifact to the catalog using the Analytics Catalog service
  4. Release the Analytic Deploy the analytic to the production environment using the Analytics Catalog service
  5. Execute the Analytic Execute an orchestration that includes the analytic using the Analytics Runtime service. The Analytics Runtime service also provides a scheduling feature for running an individual analytic or an orchestration at time-based intervals
Lab - Analytics Tools
  • Clone the following repository
    https://github.com/PredixDev/predix-analytics-sample
    
                                    cd ${HOME} && git clone https://github.com/PredixDev/predix-analytics-sample
                                
  • In the predix-analytics-sample repository go to analytics
  • Go to demo-adder-py
    
                                    zip -q -r ../demo-adder-py.zip  .
                                
  • Open postman and import the postman directory from predix-analytics-sample
  • Setup OAuth2.0 with
    • Access Token URL: https://foundry-eu.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token
    • client id: framework-client-secret
    • client secret: client-secret
  • tenant_id: a1e602f3-1fc1-4302-be4b-cbf6a9ed126a
  • catalog_uri https://predix-analytics-catalog-release.run.aws-usw02-pr.ice.predix.io/api/v1/catalog/analytics
  • Explore some possibilities of the collection, there is a lot of feature !!!
  • For each request of this collection you have to delete the Authorization in the header
Lab - Analytics Tools
  • Open the analytics UI https://foundry-eu.predix-analytics-ui.run.aws-usw02-pr.ice.predix.io
    • Login: operator
    • Password: operator
  • Import a new analytic
    • click on New Analytic
    • Name : demo-adder-py-{Your Initials}
    • Author {Your Name}
    • Version: v1
    • Languages: Python
    • Select a file to attach pick the zip file we just created
    • Check executable
    • Select a file to attach pick the demo-adder-template.json
      • In type put Template
    • Press save
Lab - Test Analytics from catalog
  • In the window Validate and Test
    
                                    {"number1": 123, "number2": 456}
                                
  • Press Submit for Validation and Test
  • Then Wait...

  • Why is it so freaking slow ? it's just an addition....
  • Go to logs tab
      demo-adder-py-{initial}
    • If nothing is displayed press Get Recent Logs
  • So much logs...
Lab - Test Analytics from Postman
  • Open Postman and select Retrieve Analytic(s) By Name in Analytics folder
    • Should end up by /api/v1/catalog/analytics/versions?name=demo-adder-py-{Your Initials}
  • Copy the id
  • Open the Analytic Validation/Deployment/Execution directory in postman
    • Click on Validate Analytics: URL: {{catalog_uri}}/api/v1/catalog/analytics/8330e99d-9899-44d8-8498-12a18c0999a2/validation
    • In the URL bar paste the ID of the analyticId
    • Do not forget to update the token
    • Press send
      
      {
          "analyticId": "8330e99d-9899-44d8-8498-12a18c0999a2",
          "validationRequestId": "fca0c0da-ec33-45a4-85c2-77aecfd306e9",
          "status": "QUEUED",
          "message": "Analytic validation request successfully queued - reference request id is fca0c0da-ec33-45a4-85c2-77aecfd306e9 for catalog entry id 8330e99d-9899-44d8-8498-12a18c0999a2",
          "inputData": "{\"number1\":1,\"number2\":2}",
          "result": null,
          "createdTimestamp": "2018-01-26T08:39:51+00:00",
          "updatedTimestamp": "2018-01-26T08:39:51+00:00"
      }
                                          
Lab - Test Analytics from Postman
  • Open Postman and select Retrieve Validation Status in Analytics Validation
  • In the URL bar paste the ID of the analyticId and the validationRequestId
  • Do not forget to update the token
  • Press send
    
    {
        "analyticId": "01cb012b-6aa6-4069-96a2-570c29d0b7d7",
        "validationRequestId": "b685ea78-daae-420d-80ac-86ec4644de5e",
        "status": "PROCESSING",
        "message": "Processing analytic validation request for analytic id 01cb012b-6aa6-4069-96a2-570c29d0b7d7, request id b685ea78-daae-420d-80ac-86ec4644de5e",
        "inputData": "{\"number1\":1,\"number2\":2}",
        "result": null,
        "createdTimestamp": "2018-01-26T08:52:00+00:00",
        "updatedTimestamp": "2018-01-26T08:52:00+00:00"
    }
                                
  • After a while ..
    
    {
        "analyticId": "01cb012b-6aa6-4069-96a2-570c29d0b7d7",
        "validationRequestId": "b685ea78-daae-420d-80ac-86ec4644de5e",
        "status": "COMPLETED",
        "message": "Analytic validation completed successfully.",
        "inputData": "{\"number1\":1,\"number2\":2}",
        "result": "{\"result\": 3}",
        "createdTimestamp": "2018-01-26T08:52:00+00:00",
        "updatedTimestamp": "2018-01-26T08:58:11+00:00"
    }
                                
Lab - Test Analytics from Postman
  • Open Postman and select Retrieve Analytic Log in Analytics folder
  • In the URL bar paste the ID of the analyticId
  • Do not forget to update the token
  • Press send
    
    Created app with guid f02af646-1911-4707-82dc-73b609fb7679
    Updated app with guid f02af646-1911-4707-82dc-73b609fb7679 ({"route"=>"d71604dc-6a25-483e-8b75-aa8af61ada07", :verb=>"add", :relation=>"routes", :related_guid=>"d71604dc-6a25-483e-8b75-aa8af61ada07"})
    Updated app with guid f02af646-1911-4707-82dc-73b609fb7679 ({"state"=>"STOPPED"})
    Updated app with guid f02af646-1911-4707-82dc-73b609fb7679 ({"environment_json"=>"PRIVATE DATA HIDDEN"})
    Updated app with guid f02af646-1911-4707-82dc-73b609fb7679 ({"environment_json"=>"PRIVATE DATA HIDDEN"})
    Updated app with guid f02af646-1911-4707-82dc-73b609fb7679 ({"environment_json"=>"PRIVATE DATA HIDDEN"})
    Updated app with guid f02af646-1911-4707-82dc-73b609fb7679 ({"environment_json"=>"PRIVATE DATA HIDDEN"})
    Updated app with guid f02af646-1911-4707-82dc-73b609fb7679 ({"state"=>"STARTED"})
    -----> Preparing Python/Miniconda Environment (3.5.2)
           installing: openssl-1.0.1h-1 ...
           installing: pycosat-0.6.1-py27_0 ...
           installing: pyyaml-3.11-py27_0 ...
           installing: readline-6.2-2 ...
           installing: conda-3.7.3-py27_0 ...
    Python 2.7.8 :: Continuum Analytics, Inc.
                                    ........
    2018-01-26 08:59:06,356 - CFAmqpClient - INFO - Connection to RabbitMQ successfully created/revived.
    2018-01-26 08:59:06,590 - CFAmqpClient - INFO - Consumers ready to consume messages
    2018-01-26 08:59:09,327 - CFAmqpClient - INFO - Connection to RabbitMQ successfully created/revived.
    2018-01-26 08:59:09,481 - CFAmqpClient - INFO - Consumers ready to consume messages
                                
Anatomy of Analytics
  • Python Package
    • config.json
      
      {
        "entry-method": "analytic.demoAdder.add",
        "non-conda-libs": [
          "boto"
        ],
        "conda-libs": [
          "numpy",
          "scipy"
        ]
      }
                                      
    • directory containing the python code
      
      __init__.py  demoAdder.py
                                      
    • The Main class is demoAdder and the called method is add
    • The analytics framework will call the method add
Lab - Deploy to production of an Analytics
  • Return to the home page of the Analytics UI
  • Click on your annalytics
  • Click on Deploy to Production
    • Set memory to 128M
    • Set disk to 512M
    • Number of instances 1
  • Press Deploy to production
  • Wait... and press Refresh
  • We redo a full process of installing package...
  • You have a status bar on the top part of the ui, try to save those ids
    
    Analytic deployment request successfully queued - reference request id is 2e65f7be-e0e7-49eb-a48e-f7cfa0d98beb for catalog entry id 01cb012b-6aa6-4069-96a2-570c29d0b7d7
                                
Deployment of Analytics
  • Once an analytics is deployed to production you cannot change it, you can only delete it...
  • But the execution time is much faster
  • You can check on the UI the state has changed
  • If you redo a Retrieve Analytic(s) By Name
    
    {
        "analyticCatalogEntries": [
            {
                "id": "01cb012b-6aa6-4069-96a2-570c29d0b7d7",
                "name": "demo-adder-py-bl",
                "author": "Jack Daniels",
                "description": "",
                "version": "v1",
                "supportedLanguage": "Python",
                "customMetadata": null,
                "taxonomyLocation": "/uncategorized",
                "state": "PRODUCTION",
                "access": "FULL",
                "createdTimestamp": "2018-01-26T08:51:34+00:00",
                "updatedTimestamp": "2018-01-26T09:16:30+00:00"
            }
        ]
    }
                            
  • If you want to change it you need to increment the version and redo the whole process
Lab - Test of Deployed Analytics
  • Go back to you ui and press on the name of the analytics
  • Click on test
    • In the Input field put :
      
      {"number1": 123, "number2": 456}
                                          
    • You will get the output immediatlly
FastToken why you really need it
Without fast token:
  1. Send the request to UAA to get a token
  2. Inject the token to your request
  3. Check the token against UAA
  4. Serve the response
With fast token:
  1. Send the request to UAA to get a token
  2. Inject the token to your request
  3. Check the token against local using the token key
  4. Serve the response
The second is way faster than the first one.