Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
18
Merge Requests
18
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
nexedi
slapos.core
Commits
ec92a0c4
Commit
ec92a0c4
authored
Sep 15, 2014
by
Cédric de Saint Martin
Committed by
Rafael Monnerat
Nov 10, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[slapos.core] Add retention_delay support.
parent
abe4ec1f
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
297 additions
and
29 deletions
+297
-29
slapos/grid/SlapObject.py
slapos/grid/SlapObject.py
+135
-0
slapos/grid/slapgrid.py
slapos/grid/slapgrid.py
+7
-4
slapos/tests/slapgrid.py
slapos/tests/slapgrid.py
+60
-22
slapos/tests/slapobject.py
slapos/tests/slapobject.py
+95
-3
No files found.
slapos/grid/SlapObject.py
View file @
ec92a0c4
...
@@ -37,6 +37,7 @@ import subprocess
...
@@ -37,6 +37,7 @@ import subprocess
import
tarfile
import
tarfile
import
tempfile
import
tempfile
import
textwrap
import
textwrap
import
time
import
xmlrpclib
import
xmlrpclib
from
supervisor
import
xmlrpc
from
supervisor
import
xmlrpc
...
@@ -273,6 +274,8 @@ class Software(object):
...
@@ -273,6 +274,8 @@ class Software(object):
class
Partition
(
object
):
class
Partition
(
object
):
"""This class is responsible of the installation of an instance
"""This class is responsible of the installation of an instance
"""
"""
retention_lock_delay_filename
=
'.slapos-retention-lock-delay'
retention_lock_date_filename
=
'.slapos-retention-lock-date'
# XXX: we should give the url (or the "key") instead of the software_path
# XXX: we should give the url (or the "key") instead of the software_path
# then compute the path from it, like in Software.
# then compute the path from it, like in Software.
...
@@ -289,6 +292,7 @@ class Partition(object):
...
@@ -289,6 +292,7 @@ class Partition(object):
buildout
,
buildout
,
logger
,
logger
,
certificate_repository_path
=
None
,
certificate_repository_path
=
None
,
retention_delay
=
0
):
):
"""Initialisation of class parameters"""
"""Initialisation of class parameters"""
self
.
buildout
=
buildout
self
.
buildout
=
buildout
...
@@ -315,6 +319,20 @@ class Partition(object):
...
@@ -315,6 +319,20 @@ class Partition(object):
self
.
partition_id
+
'.crt'
)
self
.
partition_id
+
'.crt'
)
self
.
_updateCertificate
()
self
.
_updateCertificate
()
self
.
retention_delay
=
retention_delay
if
type
(
self
.
retention_delay
)
not
in
(
int
,
float
)
\
or
self
.
retention_delay
<=
0
:
self
.
logger
.
warn
(
'Retention delay value (%s) is not valid, ignoring.'
\
%
self
.
retention_delay
)
self
.
retention_delay
=
0
self
.
retention_lock_delay_file_path
=
os
.
path
.
join
(
self
.
instance_path
,
self
.
retention_lock_delay_filename
)
self
.
retention_lock_date_file_path
=
os
.
path
.
join
(
self
.
instance_path
,
self
.
retention_lock_date_filename
)
def
_updateCertificate
(
self
):
def
_updateCertificate
(
self
):
try
:
try
:
partition_certificate
=
self
.
computer_partition
.
getCertificate
()
partition_certificate
=
self
.
computer_partition
.
getCertificate
()
...
@@ -492,6 +510,7 @@ class Partition(object):
...
@@ -492,6 +510,7 @@ class Partition(object):
buildout_binary
=
buildout_binary
,
buildout_binary
=
buildout_binary
,
logger
=
self
.
logger
)
logger
=
self
.
logger
)
self
.
generateSupervisorConfigurationFile
()
self
.
generateSupervisorConfigurationFile
()
self
.
createRetentionLockDelay
()
def
generateSupervisorConfigurationFile
(
self
):
def
generateSupervisorConfigurationFile
(
self
):
"""
"""
...
@@ -564,6 +583,11 @@ class Partition(object):
...
@@ -564,6 +583,11 @@ class Partition(object):
"""
"""
self
.
logger
.
info
(
"Destroying Computer Partition %s..."
self
.
logger
.
info
(
"Destroying Computer Partition %s..."
%
self
.
computer_partition
.
getId
())
%
self
.
computer_partition
.
getId
())
self
.
createRetentionLockDate
()
if
not
self
.
checkRetentionIsAuthorized
():
return
False
# Launches "destroy" binary if exists
# Launches "destroy" binary if exists
destroy_executable_location
=
os
.
path
.
join
(
self
.
instance_path
,
'sbin'
,
destroy_executable_location
=
os
.
path
.
join
(
self
.
instance_path
,
'sbin'
,
'destroy'
)
'destroy'
)
...
@@ -607,6 +631,8 @@ class Partition(object):
...
@@ -607,6 +631,8 @@ class Partition(object):
except
IOError
as
exc
:
except
IOError
as
exc
:
raise
IOError
(
"I/O error while freeing partition (%s): %s"
%
(
self
.
instance_path
,
exc
))
raise
IOError
(
"I/O error while freeing partition (%s): %s"
%
(
self
.
instance_path
,
exc
))
return
True
def
fetchInformations
(
self
):
def
fetchInformations
(
self
):
"""Fetch usage informations with buildout, returns it.
"""Fetch usage informations with buildout, returns it.
"""
"""
...
@@ -649,3 +675,112 @@ class Partition(object):
...
@@ -649,3 +675,112 @@ class Partition(object):
supervisor
.
addProcessGroup
(
gname
)
supervisor
.
addProcessGroup
(
gname
)
self
.
logger
.
info
(
'Updated %r'
%
gname
)
self
.
logger
.
info
(
'Updated %r'
%
gname
)
self
.
logger
.
debug
(
'Supervisord updated'
)
self
.
logger
.
debug
(
'Supervisord updated'
)
def
_set_ownership
(
self
,
path
):
"""
If running as root: copy ownership of software_path to path
If not running as root: do nothing
"""
if
os
.
getuid
():
return
root_stat
=
os
.
stat
(
self
.
software_path
)
path_stat
=
os
.
stat
(
path
)
if
(
root_stat
.
st_uid
!=
path_stat
.
st_uid
or
root_stat
.
st_gid
!=
path_stat
.
st_gid
):
os
.
chown
(
path
,
root_stat
.
st_uid
,
root_stat
.
st_gid
)
def
checkRetentionIsAuthorized
(
self
):
"""
Check if retention is authorized by checking retention lock delay or
retention lock date.
A retention lock delay is a delay which is:
* Defined by the user/machine who requested the instance
* Hardcoded the first time the instance is deployed, then is read-only
during the whole lifetime of the instance
* Triggered the first time the instance is requested to be destroyed
(retention will be ignored).
From this point, it is not possible to destroy the instance until the
delay is over.
* Accessible in read-only mode from the partition
A retention lock date is the date computed from (date of first
retention request + retention lock delay in days).
Example:
* User requests an instance with delay as 10 (days) to a SlapOS Master
* SlapOS Master transmits this information to the SlapOS Node (current code)
* SlapOS Node hardcodes this delay at first deployment
* User requests retention of instance
* SlapOS Node tries to destroy for the first time: it doesn't actually
destroy, but it triggers the creation of a retention lock date from
from the hardcoded delay. At this point it is not possible to
destroy instance until current date + 10 days.
* SlapOS Node continues to try to destroy: it doesn't do anything until
retention lock date is reached.
"""
retention_lock_date
=
self
.
getExistingRetentionLockDate
()
now
=
time
.
time
()
if
not
retention_lock_date
:
if
self
.
getExistingRetentionLockDelay
()
>
0
:
self
.
logger
.
info
(
'Impossible to destroy partition yet because of retention lock.'
)
return
False
# Else: OK to destroy
else
:
if
now
<
retention_lock_date
:
self
.
logger
.
info
(
'Impossible to destroy partition yet because of retention lock.'
)
return
False
# Else: OK to destroy
return
True
def
createRetentionLockDelay
(
self
):
"""
Create a retention lock delay for the current partition.
If retention delay is not specified, create it wth "0" as value
"""
if
os
.
path
.
exists
(
self
.
retention_lock_delay_file_path
):
return
with
open
(
self
.
retention_lock_delay_file_path
,
'w'
)
as
delay_file_path
:
delay_file_path
.
write
(
str
(
self
.
retention_delay
))
self
.
_set_ownership
(
self
.
retention_lock_delay_file_path
)
def
getExistingRetentionLockDelay
(
self
):
"""
Return the retention lock delay of current partition (created at first
deployment) if exist.
Return -1 otherwise.
"""
retention_delay
=
-
1
if
os
.
path
.
exists
(
self
.
retention_lock_delay_file_path
):
with
open
(
self
.
retention_lock_delay_file_path
)
as
delay_file_path
:
retention_delay
=
float
(
delay_file_path
.
read
())
return
retention_delay
def
createRetentionLockDate
(
self
):
"""
If retention lock delay > 0:
Create a retention lock date for the current partition from the
retention lock delay.
Do nothing otherwise.
"""
if
os
.
path
.
exists
(
self
.
retention_lock_date_file_path
):
return
retention_delay
=
self
.
getExistingRetentionLockDelay
()
if
retention_delay
<=
0
:
return
now
=
int
(
time
.
time
())
retention_date
=
now
+
retention_delay
*
24
*
3600
with
open
(
self
.
retention_lock_date_file_path
,
'w'
)
as
date_file_path
:
date_file_path
.
write
(
str
(
retention_date
))
self
.
_set_ownership
(
self
.
retention_lock_date_file_path
)
def
getExistingRetentionLockDate
(
self
):
"""
Return the retention lock delay of current partition if exist.
Return None otherwise.
"""
if
os
.
path
.
exists
(
self
.
retention_lock_date_file_path
):
with
open
(
self
.
retention_lock_date_file_path
)
as
date_file_path
:
return
float
(
date_file_path
.
read
())
else
:
return
None
slapos/grid/slapgrid.py
View file @
ec92a0c4
...
@@ -662,7 +662,8 @@ class Slapgrid(object):
...
@@ -662,7 +662,8 @@ class Slapgrid(object):
software_release_url
=
software_url
,
software_release_url
=
software_url
,
certificate_repository_path
=
self
.
certificate_repository_path
,
certificate_repository_path
=
self
.
certificate_repository_path
,
buildout
=
self
.
buildout
,
buildout
=
self
.
buildout
,
logger
=
self
.
logger
)
logger
=
self
.
logger
,
retention_delay
=
getattr
(
computer_partition
,
'_retention_delay'
,
0
))
computer_partition_state
=
computer_partition
.
getState
()
computer_partition_state
=
computer_partition
.
getState
()
# XXX this line breaks 37 tests
# XXX this line breaks 37 tests
...
@@ -1088,7 +1089,8 @@ class Slapgrid(object):
...
@@ -1088,7 +1089,8 @@ class Slapgrid(object):
software_release_url
=
software_url
,
software_release_url
=
software_url
,
certificate_repository_path
=
self
.
certificate_repository_path
,
certificate_repository_path
=
self
.
certificate_repository_path
,
buildout
=
self
.
buildout
,
buildout
=
self
.
buildout
,
logger
=
self
.
logger
)
logger
=
self
.
logger
,
retention_delay
=
getattr
(
computer_partition
,
'_retention_delay'
,
0
))
local_partition
.
stop
()
local_partition
.
stop
()
try
:
try
:
computer_partition
.
stopped
()
computer_partition
.
stopped
()
...
@@ -1101,7 +1103,7 @@ class Slapgrid(object):
...
@@ -1101,7 +1103,7 @@ class Slapgrid(object):
self
.
logger
.
info
(
'Ignoring destruction of %r, as no report usage was sent'
%
self
.
logger
.
info
(
'Ignoring destruction of %r, as no report usage was sent'
%
computer_partition
.
getId
())
computer_partition
.
getId
())
continue
continue
local_partition
.
destroy
()
destroyed
=
local_partition
.
destroy
()
except
(
SystemExit
,
KeyboardInterrupt
):
except
(
SystemExit
,
KeyboardInterrupt
):
computer_partition
.
error
(
traceback
.
format_exc
(),
logger
=
self
.
logger
)
computer_partition
.
error
(
traceback
.
format_exc
(),
logger
=
self
.
logger
)
raise
raise
...
@@ -1111,7 +1113,8 @@ class Slapgrid(object):
...
@@ -1111,7 +1113,8 @@ class Slapgrid(object):
exc
=
traceback
.
format_exc
()
exc
=
traceback
.
format_exc
()
computer_partition
.
error
(
exc
,
logger
=
self
.
logger
)
computer_partition
.
error
(
exc
,
logger
=
self
.
logger
)
try
:
try
:
computer_partition
.
destroyed
()
if
destroyed
:
computer_partition
.
destroyed
()
except
NotFoundError
:
except
NotFoundError
:
self
.
logger
.
debug
(
'Ignored slap error while trying to inform about '
self
.
logger
.
debug
(
'Ignored slap error while trying to inform about '
'destroying not fully configured Computer Partition %r'
%
'destroying not fully configured Computer Partition %r'
%
...
...
slapos/tests/slapgrid.py
View file @
ec92a0c4
This diff is collapsed.
Click to expand it.
slapos/tests/slapobject.py
View file @
ec92a0c4
...
@@ -27,6 +27,7 @@
...
@@ -27,6 +27,7 @@
import
logging
import
logging
import
os
import
os
import
time
import
unittest
import
unittest
from
slapos.slap
import
ComputerPartition
as
SlapComputerPartition
from
slapos.slap
import
ComputerPartition
as
SlapComputerPartition
...
@@ -125,7 +126,8 @@ class MasterMixin(BasicMixin, unittest.TestCase):
...
@@ -125,7 +126,8 @@ class MasterMixin(BasicMixin, unittest.TestCase):
self
,
self
,
software_release_url
,
software_release_url
,
partition_id
=
None
,
partition_id
=
None
,
slap_computer_partition
=
None
slap_computer_partition
=
None
,
retention_delay
=
None
,
):
):
"""
"""
Create a partition, and return a Partition object created
Create a partition, and return a Partition object created
...
@@ -153,10 +155,11 @@ class MasterMixin(BasicMixin, unittest.TestCase):
...
@@ -153,10 +155,11 @@ class MasterMixin(BasicMixin, unittest.TestCase):
self
.
instance_root
,
'supervisor'
)
self
.
instance_root
,
'supervisor'
)
os
.
mkdir
(
supervisor_configuration_path
)
os
.
mkdir
(
supervisor_configuration_path
)
return
Partition
(
partition
=
Partition
(
software_path
=
software_path
,
software_path
=
software_path
,
instance_path
=
instance_path
,
instance_path
=
instance_path
,
supervisord_partition_configuration_path
=
supervisor_configuration_path
,
supervisord_partition_configuration_path
=
os
.
path
.
join
(
supervisor_configuration_path
,
partition_id
),
supervisord_socket
=
os
.
path
.
join
(
supervisord_socket
=
os
.
path
.
join
(
supervisor_configuration_path
,
'supervisor.sock'
),
supervisor_configuration_path
,
'supervisor.sock'
),
computer_partition
=
slap_computer_partition
,
computer_partition
=
slap_computer_partition
,
...
@@ -168,6 +171,11 @@ class MasterMixin(BasicMixin, unittest.TestCase):
...
@@ -168,6 +171,11 @@ class MasterMixin(BasicMixin, unittest.TestCase):
logger
=
logging
.
getLogger
(),
logger
=
logging
.
getLogger
(),
)
)
partition
.
updateSupervisor
=
FakeCallAndNoop
if
retention_delay
:
partition
.
retention_delay
=
retention_delay
return
partition
class
TestSoftwareNetworkCacheSlapObject
(
MasterMixin
,
unittest
.
TestCase
):
class
TestSoftwareNetworkCacheSlapObject
(
MasterMixin
,
unittest
.
TestCase
):
"""
"""
...
@@ -382,3 +390,87 @@ class TestPartitionSlapObject(MasterMixin, unittest.TestCase):
...
@@ -382,3 +390,87 @@ class TestPartitionSlapObject(MasterMixin, unittest.TestCase):
# XXX: What should it raise?
# XXX: What should it raise?
self
.
assertRaises
(
IOError
,
partition
.
install
)
self
.
assertRaises
(
IOError
,
partition
.
install
)
class
TestPartitionDestructionLock
(
MasterMixin
,
unittest
.
TestCase
):
def
setUp
(
self
):
MasterMixin
.
setUp
(
self
)
Partition
.
generateSupervisorConfigurationFile
=
FakeCallAndNoop
()
utils
.
bootstrapBuildout
=
FakeCallAndNoop
()
utils
.
launchBuildout
=
FakeCallAndStore
()
def
test_retention_lock_delay_creation
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
deployed_delay
=
int
(
open
(
partition
.
retention_lock_delay_file_path
).
read
())
self
.
assertEqual
(
delay
,
deployed_delay
)
def
test_no_retention_lock_delay
(
self
):
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
)
partition
.
install
()
delay
=
open
(
partition
.
retention_lock_delay_file_path
).
read
()
self
.
assertTrue
(
delay
,
'0'
)
self
.
assertTrue
(
partition
.
destroy
())
def
test_retention_lock_delay_does_not_change
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
partition
.
retention_delay
=
23
# install/destroy many times
partition
.
install
()
partition
.
destroy
()
partition
.
destroy
()
partition
.
install
()
partition
.
destroy
()
deployed_delay
=
int
(
open
(
partition
.
retention_lock_delay_file_path
).
read
())
self
.
assertEqual
(
delay
,
deployed_delay
)
def
test_retention_lock_delay_is_respected
(
self
):
delay
=
2.0
/
(
3600
*
24
)
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
deployed_delay
=
float
(
open
(
partition
.
retention_lock_delay_file_path
).
read
())
self
.
assertEqual
(
int
(
delay
),
int
(
deployed_delay
))
self
.
assertFalse
(
partition
.
destroy
())
time
.
sleep
(
1
)
self
.
assertFalse
(
partition
.
destroy
())
time
.
sleep
(
1
)
self
.
assertTrue
(
partition
.
destroy
())
def
test_retention_lock_date_creation
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
partition
.
install
()
self
.
assertFalse
(
os
.
path
.
exists
(
partition
.
retention_lock_date_file_path
))
partition
.
destroy
()
deployed_date
=
float
(
open
(
partition
.
retention_lock_date_file_path
).
read
())
self
.
assertEqual
(
delay
*
3600
*
24
+
int
(
time
.
time
()),
int
(
deployed_date
))
def
test_retention_lock_date_does_not_change
(
self
):
delay
=
42
software
=
self
.
createSoftware
()
partition
=
self
.
createPartition
(
software
.
url
,
retention_delay
=
delay
)
now
=
time
.
time
()
partition
.
install
()
partition
.
destroy
()
partition
.
retention_delay
=
23
# install/destroy many times
partition
.
install
()
partition
.
destroy
()
partition
.
destroy
()
partition
.
install
()
partition
.
destroy
()
deployed_date
=
float
(
open
(
partition
.
retention_lock_date_file_path
).
read
())
self
.
assertEqual
(
delay
*
3600
*
24
+
int
(
now
),
int
(
deployed_date
))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment