========================================= Platform Support Design Changes for 2.1.3 ========================================= :version: $Revision: 1.2 $ :author: Ivan R. Judson :status: Draft Abstract ======== This document proposes design modifications to the existing Core Toolkit Design to make it easier to support on the existing platforms, and port to new platforms. These changes involve identifying the use of platform specific interfaces, and encapsulating all uses in a platform specific module. Overview ======== In order to create a simple platform strategy, a few things need to be done. Most of the work is in refactoring, abstracting, creating interfaces, and structuring modules in a coherent way. The specific tasks involved are enumerated here, by module (all of these contain platform specific or platform tailored code): 1. NetUtilities.py, NetUtilitiesLinux.py, NetUtilitiesWin32.py - These get integrated into various new config objects. 2. Platform.py - This gets refactored into the various new cofig 3. ProcessManager.py, ProcessManagerUnix.py, ProcessManagerWin32.py - This gets smoothed out so there is a single processmanager interface, implemented for each platform. - add support for signals (so kill and terminate can be made sane) - add a detached process method - replace all direct os/win32 calls with process manager 4. PersonalNode.py, PersonalNodePipes.py, PersonalNodeWin32.py - move modules without review into new organizational structure - consider replacing all direct os/win32 calls with process manager time permitting 5. CertificateManager.py - Enable this to use the GlobusConfig object, which encapsulates globus specific configuration data. 6. DataStoreClient.py - remove pageFile, replace upon user request, but it's unused now. 7. VenueClientUIClasses.py - Network Init moves into config objects. - path handling is moved to the toolkit module. - wx bug blocks use Platform.isWindows() 8. Utilities.py - auto bug reporter modified to use userconfig object Summary of Modifications ======================== os/win32 calls with process manager 4. PersonalNode.py, PersonalNodePipes.py, PersonalNodeWin32.py - move modules without review into new organizational structure - consider replacing all direct os/win32 calls with process manager time permitting 5. CertificateManager.py - Enable this to use the GlobusConfig object, which encapsulates globus specific configuration data. The modifications for supporting platforms have been extracted after carefule review of the existing platform support modules, and after looking at the toolkit initialization. It has become clear that some organization of the platform modules can make the toolkit initialization and configuration significantly easier to understand, which improves our abilities to extends, support, and debug the configuration and initilialization process. Module Organization ------------------- - AccessGrid - Config.py - class AGTkConfig - class GlobusConfig - Platform os/win32 calls with process manager 4. PersonalNode.py, PersonalNodePipes.py, PersonalNodeWin32.py - move modules without review into new organizational structure - consider replacing all direct os/win32 calls with process manager time permitting 5. CertificateManager.py - Enable this to use the GlobusConfig object, which encapsulates globus specific configuration data. The modifications for supporting platforms have been extracted after carefule review of the existing platform support modules, and after looking at the toolkit initialization. It has become clear that some organization of the platform modules can make the toolkit initialization and configuration significantly easier to understand, - Config.py - class Config - class UserConfig - class SystemConfig - class MimeConfig - ProcessManager.py - class ProcessManager - PersonalNode.py - linux2 - Config.py - ProcessManager.py - PersonalNode.py - win32 - Config.py - ProcessManager.py - PersonalNode.py Specifications -------------- from AccessGrid/Config.py: class AGTkConfig: """ This class encapsulates a system configuration for the Access Grid Toolkit. This object provides primarily read-only access to configuration data that is created when the toolkit is installed. @var version: The version of this installation. @var installDir: The directory this toolkit is installed in. @var appDir: The directory for applications for all users. @var docDir: The directory for documentation for the toolkit. @var pkgCacheDir: The directory of shared application and node service packages for all users of this installation. @var configDir: The directory for installation configuration. @var tempDir: The toolkit wide temporary file directory. @type configDir: string @type tempDir: string @type version: string @type installDir: string @type appDir: string @type docDir: string @type pkgCacheDir: string """ def __init__(self): raise "This should not be called directly, but through a subclass." def GetVersion(self): raise "This should not be called directly, but through a subclass." def GetConfigDir(self): raise "This should not be called directly, but through a subclass." def GetTempDir(self): raise "This should not be called directly, but through a subclass." def GetInstallDir(self): raise "This should not be called directly, but through a subclass." def GetSharedAppDir(self): raise "This should not be called directly, but through a subclass." def GetNodeServicesDir(self): raise "This should not be called directly, but through a subclass." def GetServicesDir(self): raise "This should not be called directly, but through a subclass." def GetDocDir(self): raise "This should not be called directly, but through a subclass." def GetPkgCacheDir(self): raise "This should not be called directly, but through a subclass." class GlobusConfig: """ This object encapsulates the information required to correctly configure Globus and pyGlobus for use with the Access Grid Toolkit. @var location: the location of the globus installation @var caCertDir: the directory of Certificate Authority Certificates @var hostname: the Hostname for the globus configuration @var proxyFile: THe filename for the globus proxy @var certFile: The filename of the X509 certificate. @var keyFile: The filename of the X509 key. """ def __init__(self, initEnvIfNeeded=0): """ This is the constructor, the only argument is used to indicate a desire to intialize the existing environment if it is discovered to be uninitialized. @param initIfNeeded: a flag indicating if this object should initialize the system if it is not. @type initIfNeeded: integer """ raise "This should not be called directly, but through a subclass." def GetLocation(self): raise "This should not be called directly, but through a subclass." def GetCACertDir(self): raise "This should not be called directly, but through a subclass." def GetHostname(self): raise "This should not be called directly, but through a subclass." def GetProxyFileName(self): raise "This should not be called directly, but through a subclass." def GetCertFileName(self): raise "This should not be called directly, but through a subclass." def GetKeyFileName(self): raise "This should not be called directly, but through a subclass." from AccessGrid/Platform/__init__.py: # Global env var AGTK = 'AGTK' AGTK_LOCATION = 'AGTK_LOCATION' AGTK_USER = 'AGTK_USER' AGTK_INSTALL = 'AGTK_INSTALL' WIN = 'win32' LINUX = 'linux2' OSX = 'darwin' def isWindows(): """Function that retusn 1 if the platform is windows, 0 otherwise """ if sys.platform == WIN: return 1 else: return 0 def isLinux(): """Function that retusn 1 if the platform is linux, 0 otherwise """ if sys.platform == LINUX: return 1 else: return 0 def isOSX(): """Function that retusn 1 if the platform is os x, 0 otherwise """ if sys.platform == OSX: return 1 else: return 0 if isWindows(): from AccessGrid.Platform.win32 import Config as Config from AccessGrid.Platform.win32 import PersonalNode as PersonalNode from AccessGrid.Platform.win32 import ProcessManager as ProcessManager elif isLinux(): from AccessGrid.Platform.linux2 import Config as Config from AccessGrid.Platform.linux2 import PersonalNode as PersonalNode from AccessGrid.Platform.linux2 import ProcessManager as ProcessManager elif isOSX(): from AccessGrid.Platform.darwin import Config as Config from AccessGrid.Platform.darwin import PersonalNode as PersonalNode from AccessGrid.Platform.darwin import ProcessManager as ProcessManager else: log.warn("Platform doesn't have a platform-specific module for %s", sys.platform) from AccessGrid/Platform/Config.py: class UserConfig(AGTkConfig): """ A user config object encapsulates all of the configuration data for a running instance of the Access Grid Toolkit software. """ def __init__(self): raise "This should not be called directly, but through a subclass." def SetRTPDefaults(self): raise "This should not be called directly, but through a subclass." class SystemConfig: """ The SystemConfig object encapsulates all system dependent configuration data, it should be extended to retrieve and store additional information as necessary. """ def __init__(self): raise "This should not be called directly, but through a subclass." def Hostname(): raise "This should not be called directly, but through a subclass." def DetectProxySettings(): raise "This should not be called directly, but through a subclass." def FileSystemFreeSpace(path): raise "This should not be called directly, but through a subclass." class MimeConfig: """ The MimeConfig object encapsulates in single object the management of mime types. This provides a cross platform solution so the AGTk can leverage legacy configuration and applications for data viewing. """ def __init__(self): raise "This should not be called directly, but through a subclass." def GetMimeType(extension = None): raise "This should not be called directly, but through a subclass." def GetMimeCommands(mimeType = None, ext = None): raise "This should not be called directly, but through a subclass." def RegisterMimeType(): raise "This should not be called directly, but through a subclass." from AccessGrid/Platform/ProcessManager.py: class ProcessManager: def __init__(self): raise "This should not be called directly, but by a subclass." def StartProcess(self, command, arglist, detached = 1): """ Start a new process. @param command : the name of the command to be started. It can either be a full pathname or a command name to be found on the default path. @param arglist : is a list of the arguments to the command. @param detached : a flag indicating whether this process should be run detached or the process manager should wait for it to complete execution to return. @type command: string @type arglist: list of strings @type detached: integer """ raise "This should not be called directly, but by a subclass." def TerminateAllProcesses(self): """ Cleanly shutdown all processes this manager has created. """ raise "This should not be called directly, but by a subclass." def TerminateProcess(self, pid): """ Cleanly shutdown the specified process this manager has created. @param pid: the id of the process to terminate. @type pid: string? integer? """ def KillAllProcesses(self): """ Kill all processes this manager has created. @warning: this is not a clean shutdown, but a forced shutdown that may result in system cruft. """ raise "This should not be called directly, but by a subclass." def KillProcess(self, pid): """ Kill a single process this manager has created. @warning: this is not a clean shutdown, but a forced shutdown that may result in system cruft. @param pid: the id of the process to terminate. @type pid: string? integer? """ raise "This should not be called directly, but by a subclass." def ListProcesses(self): """ Return a list of process id's for this process manager. @returns: a list of process id's """ raise "This should not be called directly, but by a subclass." Security Issues =============== The best case is this will tighten up security by not having redundant initialization code that could have errors hidden in multiple places. Ideally, by defining another round of interfaces this will help encapsulate problems so they are easier to track down (which also affects security when it's a security related problem). Worst case, these modifications could introduce a security related bug, but it's very unlikely given this is only configuration data. It's more likely that incorrect modifications will render the software useless as it will stop initialization. Interoperability Issues ======================= The refactoring in this proposal should not affect the interoperability of the client with servers and services. This is an internal refactoring. Related AGEPs ============= - AGEP 112: Toolkit Initialization Factoring for 2.1.3 - The Usage Logging AGEP. - The Initialization AGEP. - The Venue Client Refactoring AGEP. References ========== 1. Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Addison-Wesley Professions Computing Series, Addison Wesley Longman, Inc, Reading, MA, 1995. 2. `Python Enhancement Proposals`_ Copyright ========= This document is Copyright 2003, The University of Chicago/Argonne National Laboratory. .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 End: .. _`Python Enhancement Proposals`: http://www.python.org/peps/