push: Update push to handle files in a more cohesive way

Push now can handle a series of files and directory paths and will assume the
last argument provided is a remote path. If the argument exists, it will assume
all files and directories will be pushed to /home/mendel by default.

Change-Id: I87260f73b156648972d62118765cb7295e8a38cf
diff --git a/mdt/files.py b/mdt/files.py
index 5592c54..199e259 100644
--- a/mdt/files.py
+++ b/mdt/files.py
@@ -97,7 +97,21 @@
 
 
 class PushCommand(command.NetworkCommand):
-    '''Usage: mdt push <filename...> <remote-path>
+    '''Usage: mdt push <local-path...> [<remote-path>]
+
+If a directory name is provided as a local path, a directory will be created on
+the device and the contents of that directory will be copied in place,
+recursively.
+
+If remote-path is omitted, push assumes you mean to push files to /home/mendel
+instead.
+
+Note that the last argument is considered to be the remote path to push to!
+
+If remote-path is a path that exists locally, it will be considered to be a
+local-path instead, and push will default to pushing to /home/mendel instead.
+This can lead to surprising results, so check if your last argument exists or
+not.
 
 Variables used:
     preferred-device    - set this to your preferred device name to connect
@@ -110,37 +124,57 @@
                           device with. Defaults to 'mendel'. Only used
                           during the initial setup phase of pushing an SSH
                           key to the board.
-
-Pushes (copies) a local file or set of files to the remote device.
 '''
 
-    def preConnectRun(self, args):
-        if len(args) < 3:
-            print("Usage: mdt push <filename...> <remote-path>")
-            return False
+    def pushDir(self, sftp, dir, destination):
+        basename = os.path.basename(dir)
+        destination = os.path.join(destination, basename)
+        sftp.mkdir(destination)
 
-        for file in args[1:-1]:
-            if not os.path.isfile(file):
-                print("{0}: Is a directory -- cannot push".format(file))
-                return False
+        for path, subdirs, files in os.walk(dir):
+            relpath = os.path.relpath(path, start=dir)
+            remote_path = os.path.join(destination, relpath)
 
-        return True
+            for name in subdirs:
+                sftp.mkdir(os.path.join(remote_path, name))
+
+            for name in files:
+                self.pushFile(sftp,
+                              os.path.join(path, name),
+                              remote_path)
+
+    def pushFile(self, sftp, file, destination):
+        base_filename = os.path.basename(file)
+        remote_path = os.path.join(destination, base_filename)
+
+        sftp_callback = MakeProgressFunc(file, PROGRESS_WIDTH)
+        sftp_callback(0, 1)
+        sftp.put(file, remote_path, callback=sftp_callback)
+        sftp_callback(1, 1)
+
+        print()
 
     def runWithClient(self, client, args):
-        files_to_push = args[1:-1]
-        destination = args[-1]
+        # If the last argument (remote-path) exists, assume it is a file to push
+        # and the remote-path should be /home/mendel
+        if os.path.exists(args[-1]):
+            files_to_push = args[1:]
+            destination = '/home/mendel'
+        else:
+            files_to_push = args[1:-1]
+            destination = args[-1]
 
+        sftp = client.openSftp()
         try:
-            sftp = client.openSftp()
             for file in files_to_push:
-                base_filename = os.path.basename(file)
-                sftp_callback = MakeProgressFunc(file, PROGRESS_WIDTH)
-                remote_filename = os.path.join(destination, base_filename)
-
-                sftp_callback(0, 1)
-                sftp.put(file, remote_filename, callback=sftp_callback)
-                sftp_callback(1, 1)
-                print()
+                file = os.path.normpath(file)
+                if os.path.isdir(file):
+                    self.pushDir(sftp, file, destination)
+                else:
+                    self.pushFile(sftp, file, destination)
+        except IOError as e:
+            print("Couldn't upload file to device: {0}".format(e))
+            return 1
         finally:
             print()
             sftp.close()