#!/usr/bin/python2.6 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # import sys import os import datetime import getopt import string try: import pygtk pygtk.require("2.4") except: pass try: import gtk import gtk.glade gtk.gdk.threads_init() except: sys.exit(1) try: import glib import gobject except: sys.exit(1) # here we define the path constants so that other modules can use it. # this allows us to get access to the shared files without having to # know the actual location, we just use the location of the current # file and use paths relative to that. SHARED_FILES = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir)) LOCALE_PATH = os.path.join('/usr', 'share', 'locale') RESOURCE_PATH = os.path.join(SHARED_FILES, 'res') # the name of the gettext domain. because we have our translation files # not in a global folder this doesn't really matter, setting it to the # application name is a good idea tough. GETTEXT_DOMAIN = 'time-slider' # set up the glade gettext system and locales gtk.glade.bindtextdomain(GETTEXT_DOMAIN, LOCALE_PATH) gtk.glade.textdomain(GETTEXT_DOMAIN) import zfs from rbac import RBACprofile class SnapshotNowDialog: def __init__(self, dir_path, zfs_fs): self.dir_path = dir_path self.zfs_fs = zfs_fs self.xml = gtk.glade.XML("%s/../../glade/time-slider-snapshot.glade" \ % (os.path.dirname(__file__))) self.dialog = self.xml.get_widget("dialog") self.dir_label = self.xml.get_widget("dir_label") self.snap_name_entry = self.xml.get_widget("snapshot_name_entry") # signal dictionary dic = {"on_closebutton_clicked" : gtk.main_quit, "on_window_delete_event" : gtk.main_quit, "on_cancel_clicked" : gtk.main_quit, "on_ok_clicked" : self.__on_ok_clicked} self.xml.signal_autoconnect(dic) self.snap_name_entry.connect("activate", self.__on_entry_activate, 0) self.dir_label.set_text(self.dir_path) self.snap_name_entry.set_text("my-snapshot-%s" % datetime.datetime.now().strftime("%Y-%m-%d_%Hh%M:%S")) self.dialog.show () def validate_name (self, name, showErrorDialog=False): #check name validity # from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/common/zfs/zfs_namecheck.c#dataset_namecheck # http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/common/zfs/zfs_namecheck.c#valid_char invalid = False _validchars = string.ascii_letters + string.digits + \ "-_.:" _allchars = string.maketrans("", "") _invalidchars = _allchars.translate(_allchars, _validchars) valid_name = "" for c in name: if c not in _invalidchars: valid_name = valid_name + c else: invalid = True if invalid and showErrorDialog: dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, _("Invalid characters in snapshot name")) dialog.set_title (_("Error")) dialog.format_secondary_text(_("Allowed characters for snapshot names are :\n" "[a-z][A-Z][0-9][-_.:\n" "All invalid characters will be removed\n")) dialog.run () dialog.destroy () return valid_name def __on_entry_activate (self, widget, none): self.snap_name_entry.set_text (self.validate_name (self.snap_name_entry.get_text(), True)) return def __on_ok_clicked (self, widget): name = self.snap_name_entry.get_text() valid_name = self.validate_name (name, True) if name == valid_name: cmd = "pfexec /usr/sbin/zfs snapshot %s@%s" % (self.zfs_fs, self.validate_name (self.snap_name_entry.get_text())) fin,fout,ferr = os.popen3(cmd) # Check for any error output generated and # return it to caller if so. error = ferr.read() self.dialog.hide () if len(error) > 0: dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, _("Error occured while creating the snapshot")) dialog.set_title (_("Error")) dialog.format_secondary_text(error) dialog.run () else: dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, _("Snapshot created successfully")) dialog.set_title (_("Success")) dialog.format_secondary_text(_("A snapshot of zfs filesystem %(zfs_fs)s\n" "named %(valid_name)s\n" "has been created.\n") % { "zfs_fs" : self.zfs_fs, "valid_name" : valid_name}) dialog.run () sys.exit(1) else: self.snap_name_entry.set_text (valid_name) def main(argv): try: opts,args = getopt.getopt(sys.argv[1:], "", []) except getopt.GetoptError: sys.exit(2) #FIXME #check for 2 args here we assume the arguments are correct if len(args) != 2: dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, _("Invalid arguments count.")) dialog.set_title (_("Error")) dialog.format_secondary_text(_("Snapshot Now requires" " 2 arguments :\n- The path of the " "directory to be snapshotted.\n" "- The zfs filesystem corresponding " "to this directory.")) dialog.run() sys.exit (2) rbacp = RBACprofile() # The user security attributes checked are the following: # 1. The "Primary Administrator" role # 4. The "ZFS Files System Management" profile. # # Valid combinations of the above are: # - 1 or 4 # Note that an effective UID=0 will match any profile search so # no need to check it explicitly. if rbacp.has_profile("ZFS File System Management"): manager = SnapshotNowDialog(args[0],args[1]) gtk.main() elif os.path.exists(argv) and os.path.exists("/usr/bin/gksu"): # Run via gksu, which will prompt for the root password newargs = ["gksu", argv] for arg in args: newargs.append(arg) os.execv("/usr/bin/gksu", newargs) # Shouldn't reach this point sys.exit(1) else: dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, _("Insufficient Priviliges")) dialog.set_title (_("Error")) dialog.format_secondary_text(_("Snapshot Now requires " "administrative privileges to run. " "You have not been assigned the necessary" "administrative priviliges." "\n\nConsult your system administrator ")) dialog.run() print argv + "is not a valid executable path" sys.exit(1)