Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to Portable SR functionality #46

Merged
merged 1 commit into from
Jul 24, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 135 additions & 37 deletions plugins-base/XSFeatureDRRestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,93 @@
from XSConsoleStandard import *
import subprocess

def _listBackups(sr_uuid, vdi_uuid, legacy=False):
# list the available backups
Layout.Inst().TransientBanner(Lang("Found VDI, retrieving available backups..."))
command = ["%s/xe-restore-metadata" % (Config.Inst().HelperPath(),), "-l", "-u", sr_uuid, "-x", vdi_uuid]
if legacy:
command.append("-o")
cmd = subprocess.Popen(command,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines = True)
output, errput = cmd.communicate()
status = cmd.returncode
if status != 0:
raise Exception("(%s,%s)" % (output,errput))
Layout.Inst().PushDialogue(DRRestoreSelection(output, vdi_uuid, sr_uuid, legacy=legacy))

class DRRestoreVDISelection(Dialogue):
def __init__(self, sr_uuid, vdi_uuids):
Dialogue.__init__(self)

choices = []

self.sr_uuid = sr_uuid
self.vdi_uuids = vdi_uuids
index = 0
for choice in self.vdi_uuids:
cdef = ChoiceDef(choice, lambda i=index: self.HandleVDIChoice(i))
index = index + 1
choices.append(cdef)

self.testMenu = Menu(self, None, "", choices)
self.ChangeState('LISTVDIS')

def BuildPane(self):
pane = self.NewPane(DialoguePane(self.parent))
pane.TitleSet(Lang('Restore Virtual Machine Metadata'))
pane.AddBox()

def UpdateFieldsLISTVDIS(self):
pane = self.Pane()
pane.ResetFields()

pane.TitleSet("Available Metadata VDIs")
pane.AddTitleField(Lang("Select Metadata VDI to Restore From"))
pane.AddWarningField(Lang("You should only restore metadata from a trustworthy VDI; loading untrustworthy metadata may put your system at risk"))
pane.AddMenuField(self.testMenu)
pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )

def UpdateFields(self):
self.Pane().ResetPosition()
getattr(self, 'UpdateFields'+self.state)() # Despatch method named 'UpdateFields'+self.state

def ChangeState(self, inState):
self.state = inState
self.BuildPane()
self.UpdateFields()

def HandleVDIChoice(self, inChoice):
_listBackups(self.sr_uuid, self.vdi_uuids[inChoice], legacy=True)

def HandleKeyLISTVDIS(self, inKey):
handled = self.testMenu.HandleKey(inKey)
if not handled and inKey == 'KEY_LEFT':
Layout.Inst().PopDialogue()
handled = True
return handled

def HandleKey(self, inKey):
handled = False
if hasattr(self, 'HandleKey'+self.state):
handled = getattr(self, 'HandleKey'+self.state)(inKey)

if not handled and inKey == 'KEY_ESCAPE':
Layout.Inst().PopDialogue()
handled = True

return handled

class DRRestoreSelection(Dialogue):

def __init__(self, date_choices, vdi_uuid, sr_uuid):
def __init__(self, date_choices, vdi_uuid, sr_uuid, legacy=False):
Dialogue.__init__(self)

choices = []
self.vdi_uuid = vdi_uuid
self.sr_uuid = sr_uuid
self.legacy = legacy
self.date_choices = date_choices.splitlines()
index = 0
for choice in self.date_choices:
Expand Down Expand Up @@ -86,14 +165,19 @@ def HandleMethodChoice(self, inChoice, dryRun):
Layout.Inst().PushDialogue(InfoDialogue(Lang("Internal Error, unexpected choice: " + inChoice)))
else:
chosen_mode = inChoice
if dryRun:
dry_flag="-n "
else:
dry_flag=""
Layout.Inst().TransientBanner(Lang("Restoring VM Metadata. This may take a few minutes..."))
command = "%s/xe-restore-metadata -y %s -u %s -x %s -d %s -m %s" % (Config.Inst().HelperPath(), dry_flag, self.sr_uuid, self.vdi_uuid, self.chosen_date, chosen_mode)
status, output = commands.getstatusoutput(command)
status = os.WEXITSTATUS(status)
command = ["%s/xe-restore-metadata" % (Config.Inst().HelperPath(),), "-y", "-u", self.sr_uuid, "-x", self.vdi_uuid, "-d", self.chosen_date, "-m", chosen_mode]
if dryRun:
command.append("-n")
if self.legacy:
command.append("-o")

cmd = subprocess.Popen(command,
stdout = subprocess.PIPE,
stderr = subprocess.STDOUT,
universal_newlines = True)
output, _ = cmd.communicate()
status = cmd.returncode
Layout.Inst().PopDialogue()
if status == 0:
Layout.Inst().PushDialogue(InfoDialogue(Lang("Metadata Restore Succeeded: ") + output))
Expand Down Expand Up @@ -137,40 +221,54 @@ def __init__(self):
}
SRDialogue.__init__(self) # Must fill in self.custom before calling __init__

def _searchForVDI(self, sr_uuid, legacy=False):
# probe for the restore VDI UUID
command = ["%s/xe-restore-metadata" % (Config.Inst().HelperPath(),), "-p", "-u", sr_uuid]
if legacy:
command.append("-o")
cmd = subprocess.Popen(command,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines = True)
output, errput = cmd.communicate()
status = cmd.returncode
if status != 0:
raise Exception("(%s,%s)" % (output,errput))
if len(output) == 0:
raise Exception(errput)
return output

def _earlierConfirmHandler(self, inYesNo, sr_uuid):
if inYesNo == 'y':
Layout.Inst().TransientBanner(Lang("Searching for backup VDI...\n\nCtrl-C to abort"))
try:
vdi_uuids = [v.strip() for v in self._searchForVDI(sr_uuid, legacy=True).splitlines()]
if len(vdi_uuids) == 1:
_listBackups(sr_uuid, vdi_uuids[0], legacy=True)
else:
Layout.Inst().PushDialogue(DRRestoreVDISelection(sr_uuid, vdi_uuids))
return
except Exception as e:
Layout.Inst().PushDialogue(InfoDialogue( Lang("Metadata Restore failed: ")+Lang(e)))
else:
Layout.Inst().PushDialogue(InfoDialogue( Lang("Metadata Restore failed: a backup VDI could not be found")))
Data.Inst().Update()

def DoAction(self, inSR):
Layout.Inst().PopDialogue()
Layout.Inst().TransientBanner(Lang("Searching for backup VDI...\n\nCtrl-C to abort"))
sr_uuid = inSR['uuid']
try:
# probe for the restore VDI UUID
command = "%s/xe-restore-metadata -p -u %s" % (Config.Inst().HelperPath(), sr_uuid)
cmd = subprocess.Popen(command,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
shell = True)
output = "".join(cmd.stdout).strip()
errput = "".join(cmd.stderr).strip()
status = cmd.wait()
if status != 0:
raise Exception("(%s,%s)" % (output,errput))
if len(output) == 0:
raise Exception(errput)
vdi_uuid = output

# list the available backups
Layout.Inst().TransientBanner(Lang("Found VDI, retrieving available backups..."))
command = "%s/xe-restore-metadata -l -u %s -x %s" % (Config.Inst().HelperPath(), sr_uuid, vdi_uuid)
cmd = subprocess.Popen(command,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
shell = True)
output = "".join(cmd.stdout).strip()
errput = "".join(cmd.stderr).strip()
status = cmd.wait()
if status != 0:
raise Exception("(%s,%s)" % (output,errput))
Layout.Inst().PushDialogue(DRRestoreSelection(output, vdi_uuid, sr_uuid))
except Exception, e:
try:
vdi_uuid = self._searchForVDI(sr_uuid).strip()
except Exception as e:
# We could not uniquely identify the required VDI, ask the user if they want to check for legacy ones
message = Lang("A backup VDI could not be positively identified. Do you wish to scan for backup VDIs created with earlier versions (Warning: this operation should only be performed if you trust the contents of all VDIs in this storage repository)?")
Layout.Inst().PushDialogue(QuestionDialogue(message, lambda x: self._earlierConfirmHandler(x, sr_uuid)))
return

_listBackups(sr_uuid, vdi_uuid)
except Exception as e:
Layout.Inst().PushDialogue(InfoDialogue( Lang("Metadata Restore failed: ")+Lang(e)))
Data.Inst().Update()

Expand Down