# Presentation: 173907

## Python WS Core 

- Joshua R. Boverhof
- Lawrence Berkeley National
- Laboratory

## Outline

- Installation, packages and tools
- Example client and service
- Service specific methods
- WSRF methods and NotificationConsumer
- Handler Chain Architecture
- WS-Security
- ResourceHomeManager
- Persistence
- ResourcePortal
- Authorization

## Installation Overview

- Core Components

- utility

- resource

- security

- %python setup.py install

- BaseNotification
- NotificationConsumerService
- SecureConversation
- SubscriptionManagerService
- DelegationService
- BaseFaults
- ResourceProperties
- ResourceLifetime
- ExclusiveCanonicalization

- config.txt

- services

**Notes:**
Installation creates a “core” set of generated files for the  WSDL and XSD files specified in the “config.txt” file.  These packages are part of the installation.

## wsdl2web

- generated

- %wsdl2web.py --script=client.py --rpy=CounterService.rpy counter_service.wsdl

- server-config.tac
- ResourceTree
-  ““wsrf/core/SubscriptionManager”
-     --> SubscriptionManager Singleton
-  ”/wsrf/services”  -> dropins(“services”)
- Port
- Application

- services

- CounterService.rpy

- ./start-container.sh
- 2006/08/02 22:34 PDT [-] Log opened.
- 2006/08/02 22:34 PDT [-] twistd starting up
- 2006/08/02 22:34 PDT [-] reactor class: <class 'twisted.internet.selectreactor.SelectReactor'>
- 2006/08/02 22:34 PDT [-] Loading server-config.tac...
- 2006/08/02 22:34 PDT [-] Loaded.
- 2006/08/02 22:34 PDT [-] twisted.web.server.Site starting on 9080
- 2006/08/02 22:34 PDT [-] Starting factory <twisted.web.server.Site instance at 0x1509940>
- 2006/08/02 22:34 PDT [-] ==> Delayed Call Sweep -- 1154583250.02

- counter_service

**Notes:**
Quickstart.  Create a new Site with the wsdl2web tool. 

## client.py script

- ./client.py --help
- usage: client.py [options]
- options:
-   -h, --help            show this help message and exit
-   -u URL, --url=URL     location of service
-   -d DEBUG, --debug=DEBUG
-                         Debug Messages to file, if 1 send to stdout, if 0 then off.
-   -w WSADDRESSURI, --wsAddressURI=WSADDRESSURI
-                         Specify URI of ws-addressing version to use.
-   -c CERT, --cert=CERT  file path of certificate or proxy.
-   -k KEY, --key=KEY     file path of key
-   -v M2SECURECONV, --m2secureConv=M2SECURECONV
-                         To use secure conversation, provide URL to
-                         Authorization service, and negotiate key used to
-                         sign/verify messages.
-   -x M2SECUREMESSAGE, --m2secureMessage=M2SECUREMESSAGE
-                         use secure message to sign messages.
-   -a AUTHZ, --authz=AUTHZ  choose authorization, self or host.

**Notes:**
Generated Client script options.

## CounterService.rpy

- #################################################################
- # Automatically generated by wsdl2web.py
- # See LBNLCopyright for copyright notice!
- #################################################################
- from generated.counter_service.services.counter_service.CounterService import CounterServiceWSRF
- class Service(CounterServiceWSRF):
-     '''override methods to do something useful'''
- resource = Service()

**Notes:**
Generated service resource script

## Service Implementation

- # CounterService.py
- from pyGridWare.addressing.AddressingUtils import AddressingUtils
- from generated.counter_service.services.counter_service.CounterService\
-      import CounterServiceWSRF as WSRFService
- class Service(**WSRFService**):
-     def wsa_add(self, ps, address, **kw):
-         request,response = WSRFService.wsa_add(self, ps, address, **kw)
-         ctx = **WSRFService.GetResourceContext**(ps, address, **kw)
-         ctx.properties.Value += request
-         return request, response.__class__(ctx.properties.Value)
-     def wsa_createCounter(self, ps, address, **kw):
-         request,response = WSRFService.wsa_createCounter(self, ps, address, **kw)
-         ctx = **WSRFService.NewResourceContextCounterPortTypePort**(**kw)
-         ctx.properties.Value = 0
-         response.EndpointReference = AddressingUtils.createEndpointReference(ctx)
-         return request,response

**Notes:**
Complete counter service implementaion “CounterService.py”.  The operations “add” and “createCounter” are implemented by the site developer.  

## Example Service

- # CounterService.rpy
- import CounterService
- resource = CounterService.Service()

**Notes:**
Simple service, implementation is imported.

## Flowchart: SOAP In/out

- Core

- WSResource
- HandlerChain

- resource

- security

- generated

- resource

-  properties

- services

- stubs

- Client

- ps
- addr

- request
- response

- Twisted.web

- HTTPRequest

- service

**Notes:**
Service receives a ParsedSoap instance (representation of the soap input message), the stub packages it in a request object and returns it along with an empty serializable response object.  The contents of the request may be analyzed and acted upon, while the contents of the response object are specified.

## Client: Create & Destroy 

- from generated.counter_service.stubs import\
-     counter_service as CLIENT
- def main(**kw):
-     locator = CLIENT.CounterServiceLocator()
-     port = locator.getCounterPortType(**kw)
-     response = port.create(CLIENT.CreateRequest())
-     iport = locator.getCounterPortType(\
- 	endPointReference=response.EndpointReference,
- 	**kw)
-     iport.destroy(CLIENT.DestroyRequest())

**Notes:**
Client creates a service instance, then destroys it.

## Flowchart: Create

- generated

- resource
- manager

-  properties

- services

- stubs

- service

- wsrf

- lifetime

-  properties

- faults

- notification

- NewResourceContextCounterPortTypePort

- HTTPRequest

- ctx

**Notes:**
Call the NewResourceContext operation to obtain a resource context, and initialize any resource properties that aren’t “inherited” from a WSRF portType resource property.

## Flowchart: Destroy

- generated

- resource

-  properties

- services

- stubs

- wsrf

- lifetime

-  properties

- faults

- notification

- manager

- ctx

- ImmediateResourceTermination.Destroy(ctx, **kw)

- addr

- ctx

- Immediate

- GetResourceContext

- Destroy

**Notes:**
Destroy is defined by the WS-lifetime ImmediateResourceTermination portType.  First use the ws-address to grab the resource context, then use the a lifetime function to destroy it.

## Client: Subscribe

- from pyGridWare.wsrf.notification.NotificationConsumer import *
- from pyGridWare.wsrf.notification import SubscribeUtility
- from generated.counter_service.stubs import\
-     counter_service as COUNTER
- def main(**kw):
-     consumer = NotificationConsumer()
-     consumer.start()
-     locator = COUNTER.CounterServiceLocator()
-     port = locator.getCounterPortType(**kw)
-     response = port.createCounter(COUNTER.CreateCounterRequest())
-     epr = response.EndpointReference
-     iport = locator.getCounterPortType(endPointReference=epr, **kw)
-     request = COUNTER.SubscribeRequest()
-     SubscribeUtility.SimpleTopicExpression(request, consumer,
- 	"http://counter.com", "Value”)
-     response = iport.Subscribe(request)

**Notes:**
Client starts a notification consumer, creates a counter service instance, then subscribes to a resource property.

## Flowchart: Subscribe  

- generated

- resource

-  properties

- services

- stubs

- wsrf

- lifetime

-  properties

- faults

- notification

- Subscription Manager: create subscription & reference

- manager

- request
- context

- epr

**Notes:**
Use the SubscriptionManager singleton to create a subscritpion, providing the subscription request message and the resource context it applies to.  This returns an EPR, which is a subscription reference, that is returned back to the client in a response message.

## HandlerChain

- Core

- WSResource

- resource

- security

- Client

-  addr soap

- Twisted.web

- HTTPRequest

- request response

- HTTPResponse

- service

**Notes:**
HandlerChain is a sequence of handlers, the chain passes the results of one handler to the next.  

## WSResource render

-  def render_POST(self, request):
-         chain = self.factory.newInstance()
-         data = request.content.read()
-         try:
-             pyobj = chain.processRequest(data, request=request, resource=self)
-         except Exception, ex:
-             return self._writeFault(request, ex)
-         try:
-             soap = chain.processResponse(pyobj, request=request, resource=self)
-         except Exception, ex:
-             return self._writeFault(request, ex)
-         if soap is not None:
-             return self._writeResponse(request, soap)

- Single handler chain shared on a request/response flow.

**Notes:**
WSResource is the base of generated service classes.  It has a class attribute “factory” that creates HandlerChain instances for processing of request and response SOAP messages.

## processRequest

- WSResource

- HandlerChain

- Client

- Service
- method

- DataHandler

- Callback

- WSAddr

- str

- ps

- ps

- ps

- ps - ParsedSoap

-  factory = HandlerChainFactory

- Handler chain
- instance

**Notes:**
The DataHandler converts the SOAP string into a ParsedSoap instance, the next handler interprets WS-Address information, and the last handler calls the appropriate method on the Service.

## processResponse

- WSResource

- HandlerChain

- Client

- Service

- DataHandler

- Callback

- WSAddr

- request
- response

- request - pyobj
- response - pyobj
- sw - SoapWriter

- str

- response

- sw

-  factory = HandlerChainFactory

**Notes:**
The Service returns a “request/response” pair to the Callback handler, the response is passed to the DataHandler which serializes it, finally the SoapWriter instance is passed to the WS-Addressing handler before the response is returned to the client.

## Example WS-Security

- # SecureCounterService.rpy
- import CounterService
- from pyGridWare.security.gss.resource import WSSecurityHandlerChainFactory
- resource =  CounterService.Service()
- resource.factory = WSSecurityHandlerChainFactory

- # SecureCounterService-authService.rpy
- from pyGridWare.services import SecureConversationService
- resource = SecureConversationService.SecureConversationService()

**Notes:**
Configuration for working with the globus client counter tools, using secure conversation and digital signatures.

## WSSecurity: verify

- WSResource

- HandlerChain

- Client

- Service
- method

- DataHandler

- Callback

- WSAddr

- str

- ps

- ps

- ps

- ps - ParsedSoap

- factory = WSSecurityHandlerChainFactory

- WSSecurityHandler
- GSISignatureHandler

- ps

- Verify signatures

- Handler chain
- instance

**Notes:**
Specify the WSSecurityHandlerChainFactory to add message-level security to the Service.

## WSSecurity: sign

- WSResource

- HandlerChain

- Client

- Service
- method

- DataHandler

- Callback

- WSAddr

- str

- factory = WSSecurityHandlerChainFactory

- WSSecurityHandler
- GSISignatureHandler

- add signatures

- request - pyobj
- response - pyobj
- sw - SoapWriter

- sw

- sw

- request,
- response

- response

## ResourceHomeManager 1

- Core

- resource
-   Manager
-    ResourceHome
- SimpleRH
-     PersistentRH

- utility

- security

- generated

- Manager
- -- SimpleRH

-  properties

- services

- stubs

- service

- Context.home = ManagerHome.getInstance(SimpleResourceHome)

- Manager is configured with a implementation of IResourceHome

- Client1

- Client2

- Twisted.web

**Notes:**
To add persistence to a service the ManagerHome is configured with a different ResourceHome. 
	“ManagerHome.getInstance(PersistentResourceHome)”

## ResourceHomeManager 2

- Core

- utility

- security

- generated

- Manager
- -- SimpleRH

-  properties

- services

- stubs

- Create --> NewResourceContext

-  home
- ctx1

- Unprotected service will use a single resource home, “Anonymous”

- resource
-   Manager
-    ResourceHome
- SimpleRH
-     PersistentRH

**Notes:**
First created a resource home instance, then a new resource context is stored in it.

## Example Authorization

- # CounterService.rpy
- import CounterService
- from pyGridWare.security.authz.resource import ResourcePortal
- from pyGridWare.security.authz.checkers import GridMapChecker
- resource =  ResourcePortal(CounterService.Service(),
- 		checkers=[GridMapChecker()],
- 		callback=None)

**Notes:**
Add authorization..

## ResourcePortal: setup

- ResourcePortal(Service(), checkers=[GridMapChecker()], callback=None)

-  boverhof

- krjackson

- Twisted.web

- Site

- /wsrf/services/CounterService

**Notes:**
Client makes request, and Twisted.Web calls the ResourcePortal’s render method, if login is successful the Service will be returned and rendered.  

## Portal: Service Login

- Service Level Authorization

- ResourcePortal
- render

- GridMapChecker
- requestAvatarId

- cred
- IResource

- avatarId (boverhof)

- Portal
- login

- cred

- service

- service

**Notes:**
In the ResourcePortal render method, a login is attempted on the portal.  During login the portal first uses the checker to retrieve an ID, then uses this ID to gain access to the service (avatar).

## Portal: Service Instance

- Service Instance Level Authorization

- ResourcePortal
- render

- Service
- render

- cred
- IResourceHome

- request

- Portal
- login

- ctx1

- boverhof

**Notes:**
Access to service instances (contexts) is restricted to a resource home (avatar).  User “boverhof” can access “ctx1” and create new service instances, but he cannot access “ctx2” or “ctx3”.

## Conclusions

- Use the wsdl2web tool to create a Site
- Service specific functionality must be implemented
- Configuration files are python scripts
- The Python Grid Service work is funded by the U.S. Department of Energy Office of Science