Previous topic

Sample service

Next topic

Sample User Deployment One

This Page

Sample publication

A publication is a class responsible for making a service defined available to be consumed by users.

Not all services needs publications as you have already seen if you are following the samples. Publications are only needed for services that needs some kind of preparation, as, for example, with Virtual Machines, clone the base virtual machine so we can create COW copies from this clone. This kind of behavior needs a preparation step, that is efectively to clone the virtual base, and that will be the task of a publication for that kind of services.

You can easily follow the code to see what it does, and what you have to do if you want to provide a new one.

Download sample

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# -*- coding: utf-8 -*-

#
# Copyright (c) 2012 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, 
# are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright notice, 
#      this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright notice, 
#      this list of conditions and the following disclaimer in the documentation 
#      and/or other materials provided with the distribution.
#    * Neither the name of Virtual Cable S.L. nor the names of its contributors 
#      may be used to endorse or promote products derived from this software 
#      without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''

from django.utils.translation import ugettext as _
from uds.core.services import Publication
from uds.core.util.State import State
from datetime import datetime
import logging

logger = logging.getLogger(__name__)

class SamplePublication(Publication):
    '''
    This class shows how a publication is developed.
    
    In order to a publication to work correctly, we must provide at least the
    following methods:
        * Of course, the __init__
        * :py:meth:`.publish` 
        * :py:meth:`.checkState`
        * :py:meth:`.finish`
        
    Also, of course, methods from :py:class:`uds.core.Serializable.Serializable`
    
    
    Publication do not have an configuration interface, all data contained
    inside an instance of a Publication must be serialized if you want them between
    method calls.
    
    It's not waranteed that the class will not be serialized/deserialized
    between methods calls, so, first of all, implement the marshal and umnarshal
    mehods needed by all serializable classes.
    
    Also a thing to note is that operations requested to Publications must be
    *as fast as posible*. The operations executes in a separated thread,
    and so it cant take a bit more time to execute, but it's recommended that
    the operations executes as fast as posible, and, if it will take a long time,
    split operation so we can keep track of state.
    
    This means that, if we have "slow" operations, we must
    
    We first of all declares an estimation of how long a publication will take.
    This value is instance based, so if we override it in our class, the suggested
    time could change.
    
    The class attribute that indicates this suggested time is "suggestedTime", and
    it's expressed in seconds, (i.e. "suggestedTime = 10") 
    '''
    
    suggestedTime = 5 #: Suggested recheck time if publication is unfinished in seconds
    
    def initialize(self):
        '''
        This method will be invoked by default __init__ of base class, so it gives
        us the oportunity to initialize whataver we need here. 
        
        In our case, we setup a few attributes..
        '''
        
        # We do not check anything at marshal method, so we ensure that
        # default values are correctly handled by marshal.
        self._name = 'test'
        self._reason = '' # No error, no reason for it
        self._number = 1
        
    def marshal(self):
        '''
        returns data from an instance of Sample Publication serialized
        '''
        return '\t'.join( [self._name, self._reason, str(self._number)] )
    
    def unmarshal(self, data):
        '''
        deserializes the data and loads it inside instance.
        '''
        logger.debug('Data: {0}'.format(data))
        vals = data.split('\t')
        logger.debug('Values: {0}'.format(vals))
        self._name = vals[0]
        self._reason = vals[1]
        self._number = int(vals[2])
    
    
    def publish(self):
        '''
        This method is invoked whenever the administrator requests a new publication.
        
        The method is not invoked directly (i mean, that the administration request
        do no makes a call to this method), but a DelayedTask is saved witch will
        initiate all publication stuff (and, of course, call this method).
        
        You MUST implement it, so the publication do really something.
        All publications can be synchronous or asynchronous.
        
        The main difference between both is that first do whatever needed, (the
        action must be fast enough to do not block core), returning State.FINISHED.
        
        The second (asynchronous) are publications that could block the core, so
        it have to be done in more than one step.
        
        An example publication could be a copy of a virtual machine, where:
            * First we invoke the copy operation to virtualization provider
            * Second, we kept needed values inside instance so we can serialize
              them whenever requested
            * Returns an State.RUNNING, indicating the core that the publication
              has started but has to finish sometime later. (We do no check
              again the state and keep waiting here, because we will block the
              core untill this operation is finished).
        
        In our example wi will simple assign a name, and set number to 5. We 
        will use this number later, to make a "delay" at check if the publication
        has finished. (see method checkState)
        
        We also will make this publication an "stepped one", that is, it will not
        finish at publish call but a later checkState call
        
        Take care with instantiating threads from here. Whenever a publish returns
        "State.RUNNING", the core will recheck it later, but not using this instance
        and maybe that even do not use this server. 
        
        If you want to use threadings or somethin likt it, use DelayedTasks and
        do not block it. You also musht provide the mechanism to allow those
        DelayedTask to communicate with the publication.
        
        One sample could be, for example, to copy a bunch of files, but we know
        that this copy can take a long time and don't want it to take make it
        all here, but in a separate task. Now, do you remember that "environment"
        that is unique for every instance?, well, we can create a delayed task,
        and pass that environment (owned by this intance) as a mechanism for
        informing when the task is finished. (We insert at delayed tasks queue 
        an instance, not a class itself, so we can instantiate a class and
        store it at delayed task queue.
        
        Also note that, in that case, this class can also acomplish that by simply
        using the suggestedTime attribute and the checkState method in most cases.
        '''
        self._number = 5
        self._reason = ''
        return State.RUNNING
    
    def checkState(self):
        '''
        Our publish method will initiate publication, but will not finish it.
        So in our sample, wi will only check if _number reaches 0, and if so
        return that we have finished, else we will return that we are working
        on it.
        
        One publish returns State.RUNNING, this task will get called untill
        checkState returns State.FINISHED.
        
        Also, wi will make the publication fail one of every 10 calls to this
        method.
        
        Note: Destroying an publication also makes use of this method, so you 
        must keep the info of that you are checking (publishing or destroying...)
        In our case, destroy is 1-step action so this will no get called while
        destroying...
        '''
        import random
        self._number -= 1
        # Serialization will take care of storing self._number
        
        # One of every 10 calls 
        if random.randint(0, 9) == 9:
            self._reason = _('Random integer was 9!!! :-)')
            return State.ERROR
         
        if self._number <= 0:
            return State.FINISHED
        else:
            return State.RUNNING
           
    
    def finish(self):
        '''
        Invoked when Publication manager noticed that the publication has finished. 
        This give us the oportunity of cleaning up things (as stored vars, etc..), 
        or initialize variables that will be needed in a later phase (by deployed
        services)
        
        Returned value, if any, is ignored
        '''
        import string
        import random
        # Make simply a random string
        self._name = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10))
    
    def reasonOfError(self):
        '''
        If a publication produces an error, here we must notify the reason why 
        it happened. This will be called just after publish or checkState 
        if they return State.ERROR
        
        Returns an string, in our case, set at checkState        
        '''
        return self._reason

    def destroy(self):
        '''
        This is called once a publication is no more needed.
        
        This method do whatever needed to clean up things, such as
        removing created "external" data (environment gets cleaned by core),
        etc.. 
        
        The retunred value is the same as when publishing, State.RUNNING,
        State.FINISHED or State.ERROR.
        ''' 
        self._name = '' 
        self._reason = '' # In fact, this is not needed, but cleaning up things... :-)
        
        # We do not do anything else to destroy this instance of publication
        return State.FINISHED
        

    def cancel(self):
        '''
        Invoked for canceling the current operation.
        This can be invoked directly by an administration or by the clean up
        of the deployed service (indirectly).
        When administrator requests it, the cancel is "delayed" and not
        invoked directly.
        
        Also, take into account that cancel is the initiation of, maybe, a 
        multiple-step action, so it returns, as publish and destroy does.
        
        In our case, cancel simply invokes "destroy", that cleans up
        things and returns that the action has finished in 1 step.
        '''
        return self.destroy()

    # Here ends the publication needed methods.
    # Methods provided below are specific for this publication
    # and will be used by user deployments that uses this kind of publication

    def getBaseName(self):
        '''
        This sample method (just for this sample publication), provides
        the name generater for this publication. This is just a sample, and 
        this will do the work
        '''
        return self._name