source: server/lib/gutenbach/server/printer.py @ d42236e

no-cups
Last change on this file since d42236e was d42236e, checked in by Jessica B. Hamrick <jhamrick@…>, 12 years ago

Fix a few more bugs in printer.py and update test cases for printer.py

  • Property mode set to 100644
File size: 21.7 KB
RevLine 
[33ea505]1from .errors import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
2from .job import GutenbachJob
[b01b6d1]3from gutenbach.ipp import PrinterStates as States
[b2e077a]4import gutenbach.ipp as ipp
5import logging
6import time
[eee389a]7import threading
8import traceback
9import sys
[57bc2dc]10import tempfile
[cf0d7e8]11from . import sync
[eee389a]12
[d04a689]13
14# initialize logger
15logger = logging.getLogger(__name__)
[776a659]16
[eee389a]17class GutenbachPrinter(threading.Thread):
[b2e077a]18
[1a63bf7]19    # for IPP
[33ea505]20    printer_attributes = [
[b2e077a]21        "printer-uri-supported",
22        "uri-authentication-supported",
23        "uri-security-supported",
24        "printer-name",
25        "printer-state",
26        "printer-state-reasons",
27        "ipp-versions-supported",
28        "operations-supported",
29        "charset-configured",
30        "charset-supported",
31        "natural-language-configured",
32        "generated-natural-language-supported",
33        "document-format-default",
34        "document-format-supported",
35        "printer-is-accepting-jobs",
36        "queued-job-count",
37        "pdl-override-supported",
38        "printer-up-time",
[f6e2532]39        "compression-supported",
40        "multiple-operation-time-out",
41        "multiple-document-jobs-supported",
[1a63bf7]42    ]
[b2e077a]43
[33ea505]44    job_attributes = [
45        "job-id",
46        "job-name",
47        "job-originating-user-name",
48        "job-k-octets",
49        "job-state",
50        "job-printer-uri"
51    ]
52
[f6e2532]53    operations = [
54        "print-job",
[33ea505]55        "validate-job",
[f6e2532]56        "get-jobs",
[33ea505]57        "print-uri",
58        "create-job",
59        "pause-printer",
60        "resume-printer",
61        "get-printer-attributes",
62        "set-printer-attributes",
63        "cancel-job",
64        "send-document",
65        "send-uri",
66        "get-job-attributes",
67        "set-job-attributes",
68        "restart-job",
69        "promote-job"
[f6e2532]70    ]
71       
[609a9b0]72    def __init__(self, name, config, *args, **kwargs):
[eee389a]73
[d21198f]74        super(GutenbachPrinter, self).__init__(*args, **kwargs)
75       
76        self.name = name
[609a9b0]77        self.config = config
[d21198f]78        self.time_created = int(time.time())
[b2e077a]79
[d21198f]80        self.finished_jobs = []
81        self.pending_jobs = []
82        self.current_job = None
83        self.jobs = {}
[b2e077a]84
[d21198f]85        self.lock = threading.RLock()
[33528b4]86        self._running = False
[d21198f]87        self.paused = False
[776a659]88
[d21198f]89        # CUPS ignores jobs with id 0, so we have to start at 1
90        self._next_job_id = 1
[b01b6d1]91
[33528b4]92    @sync
[b01b6d1]93    def __repr__(self):
94        return str(self)
95
[33528b4]96    @sync
[b01b6d1]97    def __str__(self):
[33528b4]98        return "<Printer '%s'>" % self.name
[b01b6d1]99
[33ea505]100    def run(self):
[33528b4]101        self._running = True
102        while self._running:
[d42236e]103            with self.lock:
104                try:
[33528b4]105                    if self.current_job is None:
[33ea505]106                        self.start_job()
[345c476]107                    elif self.current_job.is_done:
[33ea505]108                        self.complete_job()
[d42236e]109                except:
110                    self._running = False
111                    logger.fatal(traceback.format_exc())
112                    break
[33ea505]113            time.sleep(0.1)
114
[33528b4]115    def stop(self):
116        with self.lock:
[d42236e]117            for job in self.jobs.keys():
118                try:
119                    self.jobs[job].abort()
120                    del self.jobs[job]
121                except InvalidJobStateException:
122                    pass
123               
[33528b4]124            self._running = False
125        if self.ident is not None and self.isAlive():
126            self.join()
127
[b01b6d1]128    ######################################################################
129    ###                          Properties                            ###
130    ######################################################################
131
132    @property
[7e29e6a]133    def name(self):
134        return self._name
135    @name.setter
136    def name(self, val):
137        try:
138            self._name = str(val)
139        except:
140            self._name = "gutenbach-printer"
141
142    @property
143    def config(self):
144        return self._config
145    @config.setter
146    def config(self, val):
147        try:
148            _config = dict(val).copy()
149        except:
150            raise ValueError, "not a dictionary"
151        if 'ipp-versions' not in _config:
152            raise ValueError, "missing ipp-versions"
153        self._config = _config
154
155    @property
[b01b6d1]156    def uris(self):
157        uris = ["ipp://localhost:8000/printers/" + self.name,
158                "ipp://localhost/printers/" + self.name]
159        return uris
160   
161    @property
162    def uri(self):
163        return self.uris[0]
164
165    @property
[cf0d7e8]166    @sync
[eee389a]167    def state(self):
[d42236e]168        if self.is_running and not self.paused:
169            if len(self.active_jobs) > 0:
170                state = States.PROCESSING
171            else:
172                state = States.IDLE
[cf0d7e8]173        else:
[5e70cc2]174            state = States.STOPPED
[d42236e]175
[5e70cc2]176        return state
[eee389a]177
178    @property
[cf0d7e8]179    @sync
[eee389a]180    def active_jobs(self):
[cf0d7e8]181        jobs = self.pending_jobs[:]
182        if self.current_job is not None:
183            jobs.insert(0, self.current_job.id)
[eee389a]184        return jobs
[b01b6d1]185
[33528b4]186    @property
187    def is_running(self):
188        running = self.ident is not None and self.isAlive() and self._running
189        return running
190
[b01b6d1]191    ######################################################################
192    ###                            Methods                             ###
193    ######################################################################
194
[cf0d7e8]195    @sync
[5e70cc2]196    def assert_running(self):
197        if not self.is_running:
198            raise RuntimeError, "%s not started" % str(self)
199
200    @sync
[eee389a]201    def start_job(self):
[5e70cc2]202        self.assert_running()
[33528b4]203        if not self.paused and self.current_job is None:
[cf0d7e8]204            try:
[d42236e]205                job_id = self.pending_jobs.pop(0)
[cf0d7e8]206                self.current_job = self.get_job(job_id)
207                self.current_job.play()
208            except IndexError:
209                self.current_job = None
[eee389a]210                   
[cf0d7e8]211    @sync
[eee389a]212    def complete_job(self):
[5e70cc2]213        self.assert_running()
[33528b4]214        if not self.paused and self.current_job is not None:
215            try:
216                if not self.current_job.is_done:
217                    self.current_job.stop()
218            finally:
219                self.finished_jobs.append(self.current_job.id)
220                self.current_job = None
[1a63bf7]221
[cf0d7e8]222    @sync
[eee389a]223    def get_job(self, job_id):
[5e70cc2]224        self.assert_running()
[cf0d7e8]225        if job_id not in self.jobs:
226            raise InvalidJobException(job_id)
227        return self.jobs[job_id]
[b01b6d1]228
229    ######################################################################
230    ###                        IPP Attributes                          ###
231    ######################################################################
[1a63bf7]232
[b2e077a]233    @property
234    def printer_uri_supported(self):
[5e70cc2]235        self.assert_running()
[793432f]236        return ipp.PrinterUriSupported(self.uri)
[9da7428]237    @printer_uri_supported.setter
238    def printer_uri_supported(self, val):
[5e70cc2]239        self.assert_running()
[9da7428]240        raise ipp.errors.AttributesNotSettable("printer-uri-supported")
[1a63bf7]241
[b2e077a]242    @property
243    def uri_authentication_supported(self):
[5e70cc2]244        self.assert_running()
[793432f]245        return ipp.UriAuthenticationSupported("none")
[9da7428]246    @uri_authentication_supported.setter
247    def uri_authentication_supported(self, val):
[5e70cc2]248        self.assert_running()
[9da7428]249        raise ipp.errors.AttributesNotSettable("uri-authentication-supported")
[b2e077a]250
251    @property
252    def uri_security_supported(self):
[5e70cc2]253        self.assert_running()
[793432f]254        return ipp.UriSecuritySupported("none")
[9da7428]255    @uri_security_supported.setter
256    def uri_security_supported(self, val):
[5e70cc2]257        self.assert_running()
[9da7428]258        raise ipp.errors.AttributesNotSettable("uri-security-supported")
[b2e077a]259
260    @property
261    def printer_name(self):
[5e70cc2]262        self.assert_running()
[793432f]263        return ipp.PrinterName(self.name)
[9da7428]264    @printer_name.setter
265    def printer_name(self, val):
[5e70cc2]266        self.assert_running()
[9da7428]267        raise ipp.errors.AttributesNotSettable("printer-name")
[1a63bf7]268
[b2e077a]269    @property
270    def printer_state(self):
[5e70cc2]271        self.assert_running()
[b01b6d1]272        return ipp.PrinterState(self.state)
[9da7428]273    @printer_state.setter
274    def printer_state(self, val):
[5e70cc2]275        self.assert_running()
[9da7428]276        raise ipp.errors.AttributesNotSettable("printer-state")
[1a63bf7]277
[b2e077a]278    @property
279    def printer_state_reasons(self):
[5e70cc2]280        self.assert_running()
[793432f]281        return ipp.PrinterStateReasons("none")
[9da7428]282    @printer_state_reasons.setter
283    def printer_state_reasons(self, val):
[5e70cc2]284        self.assert_running()
[9da7428]285        raise ipp.errors.AttributesNotSettable("printer-state-reasons")
[b2e077a]286
287    @property
288    def ipp_versions_supported(self):
[5e70cc2]289        self.assert_running()
[609a9b0]290        return ipp.IppVersionsSupported(*self.config['ipp-versions'])
[9da7428]291    @ipp_versions_supported.setter
292    def ipp_versions_supported(self, val):
[5e70cc2]293        self.assert_running()
[9da7428]294        raise ipp.errors.AttributesNotSettable("ipp-versions-supported")
[1a63bf7]295
[f6e2532]296    # XXX: We should query ourself for the supported operations
[b2e077a]297    @property
298    def operations_supported(self):
[5e70cc2]299        self.assert_running()
[793432f]300        return ipp.OperationsSupported(ipp.OperationCodes.GET_JOBS)
[9da7428]301    @operations_supported.setter
302    def operations_supported(self, val):
[5e70cc2]303        self.assert_running()
[9da7428]304        raise ipp.errors.AttributesNotSettable("operations-supported")
[b2e077a]305
306    @property
307    def charset_configured(self):
[5e70cc2]308        self.assert_running()
[9da7428]309        return ipp.CharsetConfigured("utf-8") # XXX
310    @charset_configured.setter
311    def charset_configured(self, val):
[5e70cc2]312        self.assert_running()
[9da7428]313        raise ipp.errors.AttributesNotSettable("charset-configured")
314       
[b2e077a]315    @property
316    def charset_supported(self):
[5e70cc2]317        self.assert_running()
[9da7428]318        return ipp.CharsetSupported("utf-8") # XXX
319    @charset_supported.setter
320    def charset_supported(self, val):
[5e70cc2]321        self.assert_running()
[9da7428]322        raise ipp.errors.AttributesNotSettable("charset-supported")
[b2e077a]323
324    @property
325    def natural_language_configured(self):
[5e70cc2]326        self.assert_running()
[793432f]327        return ipp.NaturalLanguageConfigured("en-us")
[9da7428]328    @natural_language_configured.setter
329    def natural_language_configured(self, val):
[5e70cc2]330        self.assert_running()
[9da7428]331        raise ipp.errors.AttributesNotSettable("natural-language-configured")
[b2e077a]332
333    @property
334    def generated_natural_language_supported(self):
[5e70cc2]335        self.assert_running()
[793432f]336        return ipp.GeneratedNaturalLanguageSupported("en-us")
[9da7428]337    @generated_natural_language_supported.setter
338    def generated_natural_language_supported(self, val):
[5e70cc2]339        self.assert_running()
[9da7428]340        raise ipp.errors.AttributesNotSettable("generated-natural-language-supported")
[b2e077a]341
342    @property
343    def document_format_default(self):
[5e70cc2]344        self.assert_running()
[793432f]345        return ipp.DocumentFormatDefault("application/octet-stream")
[9da7428]346    @document_format_default.setter
347    def document_format_default(self, val):
[5e70cc2]348        self.assert_running()
[9da7428]349        raise ipp.errors.AttributesNotSettable("document-format-default")
[b2e077a]350
351    @property
352    def document_format_supported(self):
[5e70cc2]353        self.assert_running()
[793432f]354        return ipp.DocumentFormatSupported("application/octet-stream", "audio/mp3")
[9da7428]355    @document_format_supported.setter
356    def document_format_supported(self, val):
[5e70cc2]357        self.assert_running()
[9da7428]358        raise ipp.errors.AttributesNotSettable("document-format-supported")
[b2e077a]359
360    @property
361    def printer_is_accepting_jobs(self):
[5e70cc2]362        self.assert_running()
[793432f]363        return ipp.PrinterIsAcceptingJobs(True)
[9da7428]364    @printer_is_accepting_jobs.setter
365    def printer_is_accepting_jobs(self, val):
[5e70cc2]366        self.assert_running()
[9da7428]367        raise ipp.errors.AttributesNotSettable("printer-is-accepting-jobs")
[b2e077a]368
369    @property
370    def queued_job_count(self):
[5e70cc2]371        self.assert_running()
[793432f]372        return ipp.QueuedJobCount(len(self.active_jobs))
[9da7428]373    @queued_job_count.setter
374    def queued_job_count(self, val):
[5e70cc2]375        self.assert_running()
[9da7428]376        raise ipp.errors.AttributesNotSettable("queued-job-count")
[b2e077a]377
378    @property
379    def pdl_override_supported(self):
[5e70cc2]380        self.assert_running()
[793432f]381        return ipp.PdlOverrideSupported("not-attempted")
[9da7428]382    @pdl_override_supported.setter
383    def pdl_override_supported(self, val):
[5e70cc2]384        self.assert_running()
[9da7428]385        raise ipp.errors.AttributesNotSettable("pdl-override-supported")
[b2e077a]386
387    @property
388    def printer_up_time(self):
[5e70cc2]389        self.assert_running()
[793432f]390        return ipp.PrinterUpTime(int(time.time()) - self.time_created)
[9da7428]391    @printer_up_time.setter
392    def printer_up_time(self, val):
[5e70cc2]393        self.assert_running()
[9da7428]394        raise ipp.errors.AttributesNotSettable("printer-up-time")
[b2e077a]395
396    @property
397    def compression_supported(self):
[5e70cc2]398        self.assert_running()
[793432f]399        return ipp.CompressionSupported("none")
[9da7428]400    @compression_supported.setter
401    def compression_supported(self, val):
[5e70cc2]402        self.assert_running()
[9da7428]403        raise ipp.errors.AttributesNotSettable("compression-supported")
[b2e077a]404
[f6e2532]405    @property
406    def multiple_operation_time_out(self):
[5e70cc2]407        self.assert_running()
[793432f]408        return ipp.MultipleOperationTimeOut(240)
[9da7428]409    @multiple_operation_time_out.setter
410    def multiple_operation_time_out(self, val):
[5e70cc2]411        self.assert_running()
[9da7428]412        raise ipp.errors.AttributesNotSettable("multiple-operation-time-out")
[f6e2532]413
414    @property
415    def multiple_document_jobs_supported(self):
[5e70cc2]416        self.assert_running()
[793432f]417        return ipp.MultipleDocumentJobsSupported(False)
[9da7428]418    @multiple_document_jobs_supported.setter
419    def multiple_document_jobs_supported(self, val):
[5e70cc2]420        self.assert_running()
[9da7428]421        raise ipp.errors.AttributesNotSettable("multiple-document-jobs-supported")
[f6e2532]422
[33ea505]423    ######################################################################
424    ###                      Job IPP Attributes                        ###
425    ######################################################################
426
427    def job_id(self, job_id):
[5e70cc2]428        self.assert_running()
[33ea505]429        job = self.get_job(job_id)
430        return ipp.JobId(job.id)
431
432    def job_name(self, job_id):
[5e70cc2]433        self.assert_running()
[33ea505]434        job = self.get_job(job_id)
435        return ipp.JobName(job.name)
436
437    def job_originating_user_name(self, job_id):
[5e70cc2]438        self.assert_running()
[33ea505]439        job = self.get_job(job_id)
440        return ipp.JobOriginatingUserName(job.creator)
441
442    def job_k_octets(self, job_id):
[5e70cc2]443        self.assert_running()
[33ea505]444        job = self.get_job(job_id)
445        return ipp.JobKOctets(job.size)
446
447    def job_state(self, job_id):
[5e70cc2]448        self.assert_running()
[33ea505]449        job = self.get_job(job_id)
450        return ipp.JobState(job.state)
451
452    def job_printer_uri(self, job_id):
[5e70cc2]453        self.assert_running()
[33ea505]454        job = self.get_job(job_id)
455        return ipp.JobPrinterUri(self.uri)
[ee8e6d0]456
[b01b6d1]457    ######################################################################
458    ###                        IPP Operations                          ###
459    ######################################################################
460
[5e70cc2]461    @sync
[57bc2dc]462    def print_job(self, document, document_name=None, document_format=None,
463                  document_natural_language=None, requesting_user_name=None,
464                  compression=None, job_name=None, job_k_octets=None):
[b01b6d1]465
[5e70cc2]466        self.assert_running()
467
[57bc2dc]468        # create the job
469        job_id = self.create_job(
470            requesting_user_name=requesting_user_name,
471            job_name=job_name,
472            job_k_octets=job_k_octets)
473       
474        # send the document
475        self.send_document(
476            job_id,
477            document,
478            document_name=document_name,
479            document_format=document_format,
480            document_natural_language=document_natural_language,
481            requesting_user_name=requesting_user_name,
482            compression=compression,
483            last_document=False)
484
485        return job_id
486
[5e70cc2]487    @sync
[7e29e6a]488    def validate_job(self, document_name=None, document_format=None,
489                     document_natural_language=None, requesting_user_name=None,
490                     compression=None, job_name=None, job_k_octets=None):
[57bc2dc]491
[5e70cc2]492        self.assert_running()
493
[57bc2dc]494        job_id = self._next_job_id
495        job = GutenbachJob(
496            job_id,
497            creator=requesting_user_name,
498            name=job_name)
499        job.spool(tempfile.TemporaryFile())
500        job.abort()
501        del job
[b01b6d1]502
[5e70cc2]503    @sync
[33ea505]504    def get_jobs(self, requesting_user_name=None, which_jobs=None,
505                 requested_attributes=None):
506       
[5e70cc2]507        self.assert_running()
508
[b01b6d1]509        # Filter by the which-jobs attribute
510        if which_jobs is None:
[34a4e5d]511            which_jobs = "not-completed"
512
513        if which_jobs == "completed":
[b01b6d1]514            jobs = [self.jobs[job_id] for job_id in self.finished_jobs]
515        elif which_jobs == "not-completed":
516            jobs = [self.jobs[job_id] for job_id in self.active_jobs]
[ee8e6d0]517        else:
[b01b6d1]518            raise ipp.errors.ClientErrorAttributes(
519                which_jobs, ipp.WhichJobs(which_jobs))
[b2e077a]520
[b01b6d1]521        # Filter by username
522        if requesting_user_name is None:
523            user_jobs = jobs
524        else:
525            user_jobs = [job for job in jobs if job.creator == requesting_user_name]
[33ea505]526
527        # Get the attributes of each job
528        job_attrs = [self.get_job_attributes(
529            job.id, requested_attributes=requested_attributes) for job in user_jobs]
[ee8e6d0]530       
[33ea505]531        return job_attrs
[ee8e6d0]532
[5e70cc2]533    @sync
[b01b6d1]534    def print_uri(self):
[5e70cc2]535        self.assert_running()
[b01b6d1]536
[5e70cc2]537    @sync
[d42236e]538    def create_job(self, requesting_user_name=None,
539                   job_name=None, job_k_octets=None):
[57bc2dc]540
[5e70cc2]541        self.assert_running()
542
[eee389a]543        job_id = self._next_job_id
544        self._next_job_id += 1
[ee8e6d0]545       
[33ea505]546        job = GutenbachJob(
547            job_id,
548            creator=requesting_user_name,
549            name=job_name)
[57bc2dc]550
[ee8e6d0]551        self.jobs[job_id] = job
[33ea505]552        return job_id
[776a659]553
[fa3e2c6]554    @sync
[b01b6d1]555    def pause_printer(self):
[fa3e2c6]556        """Pause the printer.
557
558        Does nothing if the printer is already paused.
559        """
[33528b4]560       
[5e70cc2]561        self.assert_running()
[33528b4]562        if not self.paused:
563            if self.current_job is not None and self.current_job.is_playing:
564                self.current_job.pause()
565            self.paused = True
566            logger.info("%s paused", str(self))
[fa3e2c6]567
568    @sync
[b01b6d1]569    def resume_printer(self):
[fa3e2c6]570        """Resume the printer.
571
572        Does nothing if the printer is not paused.
573        """
[33528b4]574       
[5e70cc2]575        self.assert_running()
[33528b4]576        if self.paused:
577            if self.current_job is not None:
578                self.current_job.resume()
579            self.paused = False
580            logger.info("%s unpaused", str(self))
[776a659]581
[57bc2dc]582    @sync
[b01b6d1]583    def get_printer_attributes(self, requested_attributes=None):
[5e70cc2]584        self.assert_running()
[b01b6d1]585        if requested_attributes is None:
[33ea505]586            requested = self.printer_attributes
[e58af05]587        else:
[33ea505]588            requested = [a for a in self.printer_attributes \
589                         if a in requested_attributes]
[b2e077a]590
[b01b6d1]591        _attributes = [attr.replace("-", "_") for attr in requested]
592        attributes = [getattr(self, attr) for attr in _attributes]
593        return attributes
[776a659]594
[57bc2dc]595    @sync
[7e29e6a]596    def set_printer_attributes(self, attributes):
[5e70cc2]597        self.assert_running()
[9da7428]598        for attr in attributes:
599            try:
600                setattr(self, attr, attributes[attr])
601            except AttributeError:
602                raise ipp.errors.ClientErrorAttributes
[33ea505]603
[57bc2dc]604    @sync
[33ea505]605    def cancel_job(self, job_id, requesting_user_name=None):
[5e70cc2]606        self.assert_running()
[33ea505]607        job = self.get_job(job_id)
608        try:
609            job.cancel()
610        except InvalidJobStateException:
611            # XXX
612            raise
613
[57bc2dc]614    @sync
[33ea505]615    def send_document(self, job_id, document, document_name=None,
616                      document_format=None, document_natural_language=None,
617                      requesting_user_name=None, compression=None,
618                      last_document=None):
619
[5e70cc2]620        self.assert_running()
[33ea505]621        job = self.get_job(job_id)
[345c476]622        job.spool(document)
[d42236e]623        if 'dryrun' in self.config and self.config['dryrun']:
624            job.player._dryrun = True
625        self.pending_jobs.append(job_id)
626       
[57bc2dc]627    @sync
[c1dc25f]628    def send_uri(self, job_id, document_uri, document_name=None,
629                 document_format=None, document_natural_language=None,
630                 requesting_user_name=None, compression=None,
631                 last_document=None):
[5e70cc2]632
633        self.assert_running()
[c1dc25f]634        job = self.get_job(job_id)
635        # XXX: need to validate URI
636        # XXX: need to deal with the URI stream?
[33ea505]637
[d42236e]638        #job.spool_uri(document_uri)
639        #if 'dryrun' in self.config and self.config['dryrun']:
640        #    job.player._dryrun = True
641        #self.pending_jobs.append(job_id)
642       
[57bc2dc]643    @sync
[33ea505]644    def get_job_attributes(self, job_id, requested_attributes=None):
[5e70cc2]645
646        self.assert_running()
[33ea505]647        if requested_attributes is None:
648            requested = self.job_attributes
649        else:
650            requested = [a for a in self.job_attributes \
651                         if a in requested_attributes]
652
653        _attributes = [attr.replace("-", "_") for attr in requested]
654        attributes = [getattr(self, attr)(job_id) for attr in _attributes]
655        return attributes
656
[57bc2dc]657    @sync
[9da7428]658    def set_job_attributes(self, job_id, attributes):
[5e70cc2]659
660        self.assert_running()
[9da7428]661        job = self.get_job(job_id)
662        for attr in attributes:
663            if attr in ("job-id", "job-k-octets", "job-state", "job-printer-uri"):
664                raise ipp.errors.ClientErrorAttributesNotSettable(attr)
665            elif attr == "job-name":
666                job.name = attributes[attr]
667            elif attr == "job-originating-user-name":
668                job.creator = attributes[attr] # XXX: do we want this?
[57bc2dc]669
670    @sync
[c1cebbc]671    def restart_job(self, job_id, requesting_user_name=None):
[5e70cc2]672
673        self.assert_running()
[c1cebbc]674        job = self.get_job(job_id)
675        try:
676            job.restart()
677        except InvalidJobStateException:
678            # XXX
679            raise ipp.errors.ClientErrorNotPossible
[33ea505]680
[57bc2dc]681        self.finished_jobs.remove(job_id)
682        self.pending_jobs.append(job_id)
[33ea505]683
[57bc2dc]684    @sync
[c1cebbc]685    def promote_job(self, job_id, requesting_user_name=None):
[c500bc2]686        # According to RFC 3998, we need to put the job at the front
687        # of the queue (so that when the currently playing job
688        # completes, this one will go next
689       
[5e70cc2]690        self.assert_running()
[c500bc2]691        job = self.get_job(job_id)
692        job.priority = 1 # XXX we need to actually do something
693                         # correct here
Note: See TracBrowser for help on using the repository browser.