jborean93 / pypsrp

PowerShell Remoting Protocol for Python
MIT License
326 stars 49 forks source link

Copy/Fetch functionality exposed to runspace pool #122

Closed malthe closed 2 years ago

malthe commented 3 years ago

Currently, the functionality to copy/fetch files is tied to pypsrp.client.Client.

If you want to do multiple such operations on the same RunspacePool, have an existing pool or an existing wsman connection then there is no direct way to access this functionality.

An example of a possible API:

from pypsrp.client import Copy

Copy(src, dest).invoke(pool)

For a more generic interface, we might want to pass an open binary stream instead as the source:

Copy(src_file, dest).invoke(pool)

It's a little awkward to implement because of the computation of the maximum buffer size which is based on the internal WSMan object – perhaps the connection provided to RunspacePool should actually be a WSMan rather than mostly that and possibly another object.

For example:

class RunspacePool:
    ...

    @property
    def max_fragment_size(self):
        # Get the buffer size of each fragment to send,
        # subtract. Adjust to size of the base64 encoded bytes. Also
        # subtract 82 for the fragment, message, and other header info
        # that PSRP adds.
        return int((self.connection.max_payload_size - 82) / 4 * 3)
jborean93 commented 3 years ago

Just very briefly thinking about this I think an easier solution would be to allow Client(...) to accept a RunspacePool object. That way the function can then re-utilise this pool instead of creating a new one.

malthe commented 3 years ago

@jborean93 yes I suppose it could have a from_runspace_pool class method / constructor which then initiates the client with a pool (and as a consequence, can use this directly in its methods).

jborean93 commented 3 years ago

I think just doing something like this would work

from pypsrp.client import Client
from pypsrp.powershell import RunspacePool
from pypsrp.wsman import WSMan

with WSMan(...) as wsman, RunspacePool(wsman) as rp:
    client = Client(rp)
    client.copy(..., ...)

We could also just expose copy (and other functions) as a standalone function that takes in a pool so both the old Client(wsman).copy works and this newer scenario.

jborean93 commented 2 years ago

The new psrp namespace that is coming out in the upcoming 1.0.0 release includes this functionality. You can specify an already opened Runspace Pool with it's calls and it will reutilise the pool rather than creating a new connection.

import psrp

def main():
    wsman = psrp.WSManInfo('server', ...)
    with psrp.SyncRunspacePool(wsman) as rp:
        psrp.copy_file(rp, 'src', 'dst')
        psrp.invoke_ps(rp, 'script')
        ...

This hasn't been implemented in the old pypsrp area as I'm stopping work on that in favour of the new stuff in psrp.